# Macro Basics

## Introducing BeanShell

Here is how BeanShell's author, Pat Niemeyer, describes his creation:

> BeanShell is a small, free, embeddable, Java source interpreter with object scripting language features, written in Java. BeanShell executes standard Java statements and expressions, in addition to obvious scripting commands and syntax. BeanShell supports scripted objects as simple method closures like those in Perl and JavaScript.

You do not have to know anything about Java to begin writing your own jEdit macros. But if you know how to program in Java, you already know how to write BeanShell scripts. The major strength of using BeanShell with a program written in Java is that it allows the user to customize the program's behavior using the same interfaces designed and used by the program itself. BeanShell can turn a well-designed application into a powerful, extensible toolkit.

This guide focuses on using BeanShell in macros. If you are interested in learning more about BeanShell generally, consult the BeanShell web site. Information on how to run and organize macros, whether included with the jEdit installation or written by you, can be found in .

## Single Execution Macros

As noted in , you can save a BeanShell script of any length as a text file with the `.bsh` extension and run it from the Macros menu. There are three other ways jEdit lets you use BeanShell quickly, without saving a script to storage, on a one time only basis. You will find them in the Utilities menu.

Utilities>BeanShell>Evaluate BeanShell Expression displays a text input dialog that asks you to type a single line of BeanShell commands. You can type more than one BeanShell statement so long as each of them ends with a semicolon. If BeanShell successfully interprets your input, a message box will appear with the return value of the last statement.

Utilities>BeanShell>Evaluate For Selected Lines displays a text input dialog that asks you to type a single line of BeanShell commands. The commands are evaluated for each line of the selection. In addition to the standard set of variables described in , this command defines the following:

- `line` - the line number, from the start of the buffer. The first line is numbered 0.

- `index` - the line number, from the start of the selection. The first line is numbered 0.

- `text` - the text of the line.

Try typing an expression like `(line + 1) + ": " + text` in the Evaluate For Selected Lines dialog box. This will add a line number to each selected line beginning with the number `1`.

The BeanShell expression you enter will be evaluated and substituted in place of the entire text of a selected line. If you want to leave the line's current text as an element of the modified line, you must include the defined variable `text` as part of the BeanShell expression that you enter.

Utilities>BeanShell>Evaluate Selection evaluates the selected text as a BeanShell script and replaces it with the return value of the statement.

Using Evaluate Selection is an easy way to do arithmetic calculations inline while editing. BeanShell uses numbers and arithmetic operations in an ordinary, intuitive way.

Try typing an expression like `(3745*856)+74` in the buffer, select it, and choose Utilities>BeanShell>Evaluate Selection. The selected text will be replaced by the answer, `3205794`.

Console plugin

You can also do the same thing using the BeanShell interpreter option of the Console plugin.

## The Mandatory First Example

    Macros.message(view, "Hello world!");

Running this one line script causes jEdit to display a message box (more precisely, a JOptionPane object) with the traditional beginner's message and an OK button. Let's see what is happening here.

This statement calls a static method (or function) named `message` in jEdit's Macros class. If you don't know anything about classes or static methods or Java (or C++, which employs the same concept), you will need to gain some understanding of a few terms. Obviously this is not the place for academic precision, but if you are entirely new to object-oriented programming, here are a few skeleton ideas to help you with BeanShell.

- An object is a collection of data that can be initialized, accessed and manipulated in certain defined ways.

- A class is a specification of what data an object contains and what methods can be used to work with the data. A Java application consists of one or more classes (in the case of jEdit ,over 600 classes) written by the programmer that defines the application's behavior. A BeanShell macro uses these classes, along with built-in classes that are supplied with the Java platform, to define its own behavior.

- A subclass (or child class) is a class which uses (or inherits) the data and methods of its parent class along with additions or modifications that alter the subclass's behavior. Classes are typically organized in hierarchies of parent and child classes to organize program code, to define common behavior in shared parent class code, and to specify the types of similar behavior that child classes will perform in their own specific ways.

- A method (or function) is a procedure that works with data in a particular object, other data (including other objects) supplied as parameters, or both. Methods typically are applied to a particular object which is an instance of the class to which the method belongs.

- A static method differs from other methods in that it does not deal with the data in a particular object but is included within a class for the sake of convenience.

Java has a rich set of classes defined as part of the Java platform. Like all Java applications, jEdit is organized as a set of classes that are themselves derived from the Java platform's classes. We will refer to Java classes and jEdit classes to make this distinction. Some of jEdit's classes (such as those dealing with regular expressions and XML) are derived from or make use of classes in other open-source Java packages. Except for BeanShell itself, we won't be discussing them in this guide.

In our one line script, the static method `Macros.message()` has two parameters because that is the way the method is defined in the Macros class. You must specify both parameters when you call the function. The first parameter, view, is a variable naming the current, active View object. Information about pre-defined variables can be found in .

The second parameter, which appears to be quoted text, is a string literal - a sequence of characters of fixed length and content. Behind the scenes, BeanShell and Java take this string literal and use it to create a String object. Normally, if you want to create an object in Java or BeanShell, you must construct the object using the `new` keyword and a constructor method that is part of the object's class. We'll show an example of that later. However, both Java and BeanShell let you use a string literal anytime a method's parameter calls for a String.

If you are a Java programmer, you might wonder about a few things missing from this one line program. There is no class definition, for example. You can think of a BeanShell script as an implicit definition of a `main()` method in an anonymous class. That is in fact how BeanShell is implemented; the class is derived from a BeanShell class called XThis. If you don't find that helpful, just think of a script as one or more blocks of procedural statements conforming to Java syntax rules. You will also get along fine (for the most part) with C or C++ syntax if you leave out anything to do with pointers or memory management - Java and BeanShell do not have pointers and deal with memory management automatically.

Another missing item from a Java perspective is a `package` statement. In Java, such a statement is used to bundle together a number of files so that their classes become visible to one another. Packages are not part of BeanShell, and you don't need to know anything about them to write BeanShell macros.

Finally, there are no `import` statements in this script. In Java, an `import` statement makes public classes from other packages visible within the file in which the statement occurs without having to specify a fully qualified class name. Without an import statement or a fully qualified name, Java cannot identify most classes using a single name as an identifier.

jEdit automatically imports a number of commonly-used packages into the namespace of every BeanShell script. Because of this, the script output of a recorded macro does not contain `import` statements. For the same reason, most BeanShell scripts you write will not require `import` statements.

Java requires `import` statement to be located at the beginning of a source file. BeanShell allows you to place `import` statements anywhere in a script, including inside a block of statements. The `import` statement will cover all names used following the statement in the enclosing block.

If you try to use a class that is not imported without its fully-qualified name, the BeanShell interpreter will complain with an error message relating to the offending line of code.

Here is the full list of packages automatically imported by jEdit:

    java.awt
    java.awt.event
    java.net
    java.util
    java.io
    java.lang
    javax.swing
    javax.swing.event
    org.gjt.sp.jedit
    org.gjt.sp.jedit.browser
    org.gjt.sp.jedit.buffer
    org.gjt.sp.jedit.gui
    org.gjt.sp.jedit.help
    org.gjt.sp.jedit.io
    org.gjt.sp.jedit.msg
    org.gjt.sp.jedit.options
    org.gjt.sp.jedit.pluginmgr
    org.gjt.sp.jedit.print
    org.gjt.sp.jedit.search
    org.gjt.sp.jedit.syntax
    org.gjt.sp.jedit.textarea
    org.gjt.sp.util

## Predefined Variables in BeanShell

The following variables are always available for use in BeanShell scripts:

- `buffer` - a Buffer object represents the contents of the currently visible open text file.

- `view` - A View represents the current top-level editor window, extending Java's JFrame class, that contains the various visible components of the program, including the text area, menu bar, toolbar, and any docked windows.

  This variable has the same value as the return value of:

      jEdit.getActiveView()

- `editPane` - an EditPane object contains a text area and buffer switcher. A view can be split to display edit panes. Among other things, the EditPane class contains methods for selecting the buffer to edit.

  Most of the time your macros will manipulate the `buffer` or the `textArea`. Sometimes you will need to use `view` as a parameter in a method call. You will probably only need to use `editPane` if your macros work with split views.

  This variable has the same value as the return value of:

      view.getEditPane()

- `textArea` - a JEditTextArea is the visible component that displays the current buffer.

  This variable has the same value as the return value of:

      editPane.getTextArea()

- `wm` - a DockableWindowManager is the visible component that manages dockable windows in the current view. This class is discussed in detail in . This object is useful for writing macros that interface with, open, or close plugin windows.

  This variable has the same value the return value of:

      view.getDockableWindowManager()

- `scriptPath` - set to the full path of the script currently being executed.

Note that these variables are set at the beginning of macro execution. If the macro switches views, buffers or edit panes, the variable values will be out of date. In that case, you can use the equivalent method calls.

## Helpful Methods in the Macros Class

Including `message()`, there are five static methods in the Macros class that allow you to converse easily with your macros. They all encapsulate calls to methods of the Java platform's JOptionPane class.

- public static void `message` Component comp String message
- public static void `error` Component comp String message
- public static String `input` Component comp String prompt
- public static String `input` Component comp String prompt String defaultValue
- public static int `confirm` Component comp String prompt int buttons

The format of these four declarations provides a concise reference to the way in which the methods may be used. The keyword `public` means that the method can be used outside the Macros class. The alternatives are `private` and `protected`. For purposes of BeanShell, you just have to know that BeanShell can only use public methods of other Java classes. The keyword `static` we have already discussed. It means that the method does not operate on a particular object. You call a static function using the name of the class (like Macros) rather than the name of a particular object (like `view`). The third word is the type of the value returned by the method. The keyword `void` is Java's way of saying the the method does not have a return value.

The `error()` method works just like `message()` but displays an error icon in the message box. The `input()` method furnishes a text field for input, an OK button and a Cancel button. If Cancel is pressed, the method returns `null`. If OK is pressed, a String containing the contents of the text field is returned. Note that there are two forms of the `input()` method; the first form with two parameters displays an empty input field, the other forms lets you specify an initial, default input value.

For those without Java experience, it is important to know that `null` is not the same as an empty, zero-length String. It is Java's way of saying that there is no object associated with this variable. Whenever you seek to use a return value from `input()` in your macro, you should test it to see if it is `null`. In most cases, you will want to exit gracefully from the script with a `return` statement, because the presence of a null value for an input variable usually means that the user intended to cancel macro execution. BeanShell will complain if you call any methods on a `null` object.

The `confirm()` method in the Macros class is a little more complex. The `buttons` parameter has an int type, and the usual way to supply a value is to use one of the predefined values taken from Java's JOptionPane class. You can choose among `JOptionPane.YES_NO_OPTION`, `JOptionPane.YES_NO_CANCEL_OPTION`, or `JOptionPane.OK_CANCEL_OPTION`. The return value of the method is also an int, and should be tested against the value of other predefined constants: `JOptionPane.YES_OPTION`, `JOptionPane.NO_OPTION`, `JOptionPane.OK_OPTION` or `JOptionPane.CANCEL_OPTION`.

We've looked at using `Macros.message()`. To use the other methods, you would write something like the following:

    Macros.error(view, "Goodbye, cruel world!");

    String result = Macros.input(view, "Type something here.");

    String result = Macros.input(view, "When were you born?",
        "I don't remember, I was very young at the time");

    int result = Macros.confirm(view, "Do you really want to learn"
        + " about BeanShell?",JOptionPane.YES_NO_OPTION);

In the last three examples, placing the word String or int before the variable name `result` tells BeanShell that the variable refers to an integer or a String object, even before a particular value is assigned to the variable. In BeanShell, this declaration of the type of `result` is not necessary; BeanShell can figure it out when the macro runs. This can be helpful if you are not comfortable with specifying types and classes; just use your variables and let BeanShell worry about it.

Note that macros are not limited to using these methods for presenting a user interface. In fact, full-blown user interfaces using the Java Swing APIs are also possible, and will be covered later on in .

## BeanShell Dynamic Typing

Without an explicit type declaration like String `result`, BeanShell variables can change their type at runtime depending on the object or data assigned to it. This dynamic typing allows you to write code like this (if you really wanted to):

    // note: no type declaration
    result = Macros.input(view, Type something here.);

    // this is our predefined, current View
    result = view;

    // this is an int (for integer);
    // in Java and BeanShell, int is one of a small number
    // of primitive data types which are not classes
    result = 14;

However, if you first declared `result` to be type String and and then tried these reassignments, BeanShell would complain. While avoiding explicit type declaration makes writing macro code simpler, using them can act as a check to make sure you are not using the wrong variable type of object at a later point in your script. It also makes it easier (if you are so inclined) to take a BeanShell prototype and incorporate it in a Java program.

One last thing before we bury our first macro. The double slashes in the examples just above signify that everything following them on that line should be ignored by BeanShell as a comment. As in Java and C/C++, you can also embed comments in your BeanShell code by setting them off with pairs of `/* */`, as in the following example:

    /* This is a long comment that covers several lines
    and will be totally ignored by BeanShell regardless of how
    many lines it covers */

## Now For Something Useful

Here is a macro that inserts the path of the current buffer in the text:

    String newText = buffer.getPath();
    textArea.setSelectedText(newText);

Unlike in our first macro example, here we are calling class methods on particular objects. First, we call `getPath()` on the current Buffer object to get the full path of the text file currently being edited. Next, we call `setSelectedText()` on the current text display component, specifying the text to be inserted as a parameter.

In precise terms, the `setSelectedText()` method substitutes the contents of the String parameter for a range of selected text that includes the current caret position. If no text is selected at the caret position, the effect of this operation is simply to insert the new text at that position.

Here's a few alternatives to the full file path that you could use to insert various useful things:

    // the file name (without full path)
    String newText = buffer.getName();

    // today's date
    import java.text.DateFormat;

    String newText = DateFormat.getDateInstance()
        .format(new Date());

    // a line count for the current buffer
    String newText = "This file contains "
        + textArea.getLineCount() + " lines.";

Here are brief comments on each:

- In the first, the call to `getName()` invokes another method of the Buffer class.

- The syntax of the second example chains the results of several methods. You could write it this way:

      import java.text.DateFormat;
      Date d = new Date();
      DateFormat df = DateFormat.getDateInstance();
      String result = df.format(d);

  Taking the pieces in order:

  - A Java Date object is created using the `new` keyword. The empty parenthesis after Date signify a call on the constructor method of Date having no parameters; here, a Date is created representing the current date and time.

  - `DateFormat.getDateInstance()` is a static method that creates and returns a DateFormat object. As the name implies, DateFormat is a Java class that takes Date objects and produces readable text. The method `getDateInstance()` returns a DateFormat object that parses and formats dates. It will use the default locale or text format specified in the user's Java installation.

  - Finally, DateFormat.format() is called on the new DateFormat object using the Date object as a parameter. The result is a String containing the date in the default locale.

  - Note that the Date class is contained in the `java.util` package, so an explicit import statement is not required. However, DateFormat is part of the `java.text` package, which is not automatically imported, so an explicit `import` statement must be used.

- The third example shows three items of note:

  - `getLineCount()` is a method in jEdit's JEditTextArea class. It returns an int representing the number of lines in the current text buffer. We call it on `textArea`, the pre-defined, current JEditTextArea object.

  - The use of the `+` operator (which can be chained, as here) appends objects and string literals to return a single, concatenated String.