Input / Output

Strings

In learning languages, the most important thing is the actual speaking— actually conveying ideas. As such, input and output functions are probably the most important things in a language. Personally, I found C the most difficult language just because of fgets and scanf— the input functions.

Elisp has a distinct advantage in living within a text editor. We can dump text to the current buffer or use the message window. For interactive programs, the current buffer becomes the playground for input and output control, and the means of carrying around messages are strings.

A string is a series of letters and symbols where every printable symbol can be an element, but so can carriage returns and tabs. Let's make a string:

(setq hello-world "Hello, world!")

Simple enough— we saw this last section. Another popular function is taking part of a string:

(substring hello-world 0 5)

The above substring returns the word "Hello". As in other programming languages, this function uses zero instead of one to denote the first letter. So, "H" is the zeroth character the string. Annoyingly, the first character is inclusive, but the last character is exclusive. So, (substring 0 5) includes the zeroth, first, second, third and fourth character in the string for output. This is the norm in other programming languages with a substring function.

Also, worth noting, the substring function's last argument is optional. Chopping the beginning off is as easy as setting a start value but not setting an end value. For the above example, "world!" is accessed through substring as:

(substring hello-world 7)

Negative values can be used to access the string in reverse.

We've seen concat previously. If you skipped the last chapter, here it is again building the string "Hello, world! I am Mark":

(concat hello-world " " "I am Mark")

The concat function can take any number of strings and make them one.

Sometimes, it's useful to take a string and do some processing on it word-by-word. If you work in a command-driven environment, it's useful to break up a string representing a command so you can more-easily function based on what the first word is. That is, if a user types "eat fruit" to your program, there's a function to turn this into a list containing "eat" and "fruit". We'll learn about lists later on, but this is still good to know:

(split-string "eat fruit")

By default, it breaks up strings into lists based on whitespace (invisible characters like space and carriage return). Adding a third parameter can modify this behavior, but it requires a knowledge of regular expressions. which is a little beyond the scope of this chapter.

(upcase hello-world)

(downcase hello-world)

These functions do precisely what one would expect. Translating strings to a single case make pattern-matching much easier. For titles and things, there is also:

(upcase-initials hello-world)

"This is all well and good," one might ask, "but if you define a string using quotation marks, what if I wanted to print actual quotation marks?"

Escaping with slashes!

(message "This program prints \"hello, world!\"")

"Rabbit hole forming, captain! What if we want to print an actual slash?"

Let's close up that hole. Use two slashes together:

(message "Saving files to c:\\Users\\Marko...")

Escaping does a few other things. "\n" means a line break, "\t" a tab, and a few others exist but aren't widely used.

Output

Now that we have a basic idea of what we can do with strings, let's output them. The easiest way is, of course, message. The messages go into the minibuffer and the *Messages* buffer. Duplicate messages are removed. If the end-goal is an interactive program within a buffer, the insert function is much more important:

(insert "Hello, world!")

Now, we're using the current buffer as a dump for program output. This is useful after a call to switch-to-buffer— which creates a buffer if it doesn't already exist:

(switch-to-buffer "I-O Example")

The above insert function will dumbly insert text wherever the cursor is. Usually, programmers will want to place text at the bottom of the buffer:

(goto-char (point-max))

goto-char accepts an integer and moves the cursor to that character index. point-max simply returns the last character position in the buffer. Conversely, if you said

(goto-char 0)

...the cursor would be placed at the beginning of the buffer.

Formatting

For some things, especially tables and the like, Elisp has an old friend from the C programming language. printf in C is format in Elisp. The format function takes any number of arguments, but the first explains what the result is going to look like in a weird little meta-language. An example (for a table, of course) looks something like this:

(insert (format "\n| %15s | %15s | %15s |" "Ship name" "Turn Radius" "Speed"))
(insert (format "\n| %15s | %15d | %-15.3f%% |" "Winowan" 8.999 9.8888213))

This shows about all the things you'll ever need to know. The first string argument is usually filled with a mix of string literals (things that actually become part of the resulting string) and placeholders for future data. These placeholders begin with a % and end with a single character denoting the type of data (s for string, d for integer, f for decimal, e for scientific notation, g for decimal or scientific notation, as appropriate, and there are a few others). The special thing shown here is %%, which turns the percent sign into a string literal.

Then, there are the numbers. In a case like %15s, it guarantees the width of that part of that placeholder will be 15 characters. In the first line of the example, it's hugely awesome rather than having a human have to manually make the headers... and probably re-make them when the size of the data changes.

For %15d, it corresponds to the value of 8.999. If you execute this example, you'll see that it does front-end estimation blindly trimming off any decimal precision. In the case of %-15.3f, I really wanted to show off all the features. The negative sign just left-aligns the columns rather than the default right-aligned (sorry, no centering). The fifteen is, again, the width of the entire column. The .3 tells the evaluator what sort of precision you want.

Notice that here, format rounds the number properly because it is actually rounding. The previous example (with the 8.999) was a type conversion from a decimal to an integer. This, we're keeping it decimal, so the system understands what it needs to do.

Adding Style

It's important to note that not everything in Emacs Lisp is a matter of function calls. Some constructs have ``properties''. We'll see these later with buttons. For now, it's important to note that regular text can have some properties accessed with the propertize function. Color is one of these. To add color to text while you're typing in a buffer that allows it, you might use M-x facemenu-set-foreground. To set it while inserting text, you'll need something more like:

(insert (propertize "Hello, world" 'face '(:foreground "green")))

Typesets, colors, weights, and sizes are rolled up into something called a ``face''. So, we're modifying the face of the text we're inserting. Besides :foreground, there's obviously :background, :height (measured in 1/10 of a point), :family (check out (font-family-list) for a list of valid fonts for a particular window) and a whole bunch of others available in the info pages.

As for colors, on most systems, the colors available are a part of a rather-famous general collection called "rgb.txt". To see them in Emacs, you can use M-x list-colors-display, or M-x facemenu-set-foreground and press tab a few times for the completion buffer.

Another example, this time of a header, might look like this:

(insert (propertize "Things:\n" 'face '(:height 200 :family "Arial" ) ) )

Of course, on terminal versions of Emacs, this does just about nothing.

Input

Prompts

The simplest way to quickly grab a string from the user is:

(read-from-minibuffer "Prompt: ")

The word "Prompt:" above should have some relevence to the context in which the program is prompting the user. For yes/no answers, Elisp has an answer to that, too:

(yes-or-no-p "Oh yeah? ")

To avoid having making the user type out "yes" and "no", there's this handy guy (both these functions return t or nil):

(y-or-n-p "Y or N, buddy? ")

How about passwords? This one is more robust than the previous few:

(read-passwd "Password: " t "defaultpassword")

The t and "defaultpassword" are optional. t can also be nil and if this is not nil, Emacs will ask the user to confirm the password. The "defaultpassword" is returned if the user leaves the prompt blank. In the simplest form, it can be used the same way as read-from-minibuffer

Since these functions return the strings that the user types, setq can be used to capture these.

(setq username (read-from-minibuffer "Username: "))
(setq password (read-passwd "Password: "))

From the Main Buffer

If you're using the insert function to put output to a buffer, it may be worthwhile to collect input from the buffer, too.

(backward-to-indentation 0)
(setq input (buffer-substring (point) (+ (buffer-size) 1)) )

What's all this stuff? You may recognize the substring. backward-to-indentation will put the cursor ("point") at the beginning of the current line and then set some string (called input here) to the value of the substring from the beginning of the line to the end of the buffer. The idea is that the user's input will be at the bottom of the current content buffer. The (+ (buffer-size) 1)) adds one to the length of the buffer because, after all, the second parameter in substring isn't inclusive.

To make this fully work, there's one last part of this: we need to put this in a function and bind the enter key to that function. We'll learn about functions in the next chapter, but first...

Key Input

Lastly, some Elisp programs— mainly games— may want to use the keyboard's unbuffered input so that when a user preses a button, something happens. For input made to the buffer, you'll have to capture the enter key:

(local-set-key (kbd "RET") 'function-for-handling-input)

The function-for-handling-input is irrelevant for now. The kbd function in the middle of this may cause some concern. To get valid key names, use M-x global-set-key and then press a button. You'll see "Set key ___ to command:". Then, press C-g to get out of that prompt. The value after the word "key" and before "to" (here represented by ___) gets put into your code in quotes. For letters, the string usually corresponds to the letter in a very simple and intuitive way.

For the enter key, the value is "RET". For the function keys (F1 through F12) and most other unprintable keys, the corresponding strings are the key-names in angle-brackets. This appears with the left arrow key in the example at the end of this segment.

Let's wrap this up in an example demonstrating all of this. Don't be too afraid about function definitions (defuns), we'll meet those soon enough. Also lines begining with semicolons are comments: they aren't executed and can contain anything you like:


; Quick Input/Output example

(message "Welcome to Emacs Lisp!")
(setq input "") ;establish this as a global variable!


; Don't worry about defun-- we'll learn about it in part 4.
; Just focus on the input/output stuff.
(defun ioex-left-arrow ()
(interactive)
(message "Left button pressed!")
(insert "Hello")
)

(defun ioex-evaluate ()
(interactive)

; Scoop up the last line of input- first go to the beginning of the line-
; backward-to-indentation defults to "1" which is actually 2 lines!
(backward-to-indentation 0)

;Add 2 to the point because of the '> ' prompt, and set the whole thing
; to the "input" variable.
(setq input (buffer-substring (+ (point) 2) (+ (buffer-size) 1)) )

; Go back to the end of buffer to append output.
(goto-char (point-max)) ; Manual recommends this instead of end-of-buffer!

; Insert newline and response.
(insert "\n")
(ioex-get-response)

; Print the prompt for next input.
(insert "\n> ")
)


(defun ioex-get-response ()
; This shows off LISP's cool "cond" statement. Nifty shorthand for if/then/else
; (cond ( (IF CONDITION) (THEN STUFF) ) ( (ELSEIF CONDITION) (ELSEIF STUFF) ) (...) )
; ...don't worry, we'll learn about this in part 3.
; downcase makes your stuff professional.
(cond
( (string= (downcase input) "hello") (insert "Hi!") )
( (string= (downcase input) "prompt") (insert (concat "Hello, " (read-from-minibuffer "Name: ") "!" ) ) )
( t (insert "Input received.") )
)
)

;Begin executing...
(switch-to-buffer "IO Example")
(local-set-key (kbd "<left>") 'ioex-left-arrow )
(local-set-key (kbd "RET") 'ioex-evaluate)

; Message to user...
(insert "Hello, world!\n> " )

Input and Output

If you're learning Elisp and you've been hanging out in the info pages (C-h i), you'll notice Emacs has the equivalent of hyperlinks— a place where folks can click or put the point over and press enter. They're internally (and intelligently) called "buttons", and they're made like this:

(insert-button "Push me!"
'action (lambda (a)
(goto-char (point-max))
(insert "\nHello, world!")
)
)

There are two scary things here: the 'action atom being passed to the insert-button function, and the word lambda. In this case, 'action is called a "property", and every symbol in Elisp can have properties. They're essentially key-value pairs. In this case, the key is the atom "'action" and the value associated with that key is the "lambda expression" that follows. Don't be afraid of that term, as a lambda expression is just a function (like message or insert-button) without a name that can be passed as an argument to another function. Making your own, named functions comes a little later in part 4.