PageRenderTime 117ms CodeModel.GetById 105ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 1ms

/jEdit/tags/jedit-4-1-pre5/doc/users-guide/macro-tips.xml

#
XML | 986 lines | 815 code | 147 blank | 24 comment | 0 complexity | 1e8898c76e6f25a51665c51b3a73f0ae MD5 | raw file
  1<!-- jEdit 4.0 Macro Guide, (C) 2001, 2002 John Gellene       -->
  2<!-- Wed Jun 20 16:56:26 EDT 2001 @914 /Internet Time/        -->
  3<!--                                                          -->
  4<!-- jEdit buffer-local properties:                           -->
  5<!-- :indentSize=1:noTabs=yes:maxLineLen=72:tabSize=2:        -->
  6<!--                                                          -->
  7<!-- This file covers the chapter "Macro tips and techniques" -->
  8<!-- $Id: macro-tips.xml 4310 2002-08-16 18:02:18Z spestov $
  9 -->
 10
 11<chapter id="macro-tips"><title>Macro Tips and Techniques</title>
 12
 13<sect1 id="macro-tips-input">
 14<title>Getting Input for a Macro</title>
 15
 16  <para>
 17    The dialog-based macro discussed in <xref linkend="dialog-macro"/>
 18    reflects a conventional approach to obtaining input in a Java program.
 19    Nevertheless, it can be too lengthy or tedious for someone
 20    trying to write a macro quickly. Not every macro needs a user interface
 21    specified in such detail; some macros require only a single keystroke or
 22    no input at all. In this section we outline some other techniques for
 23    obtaining input that will help you write macros quickly.
 24  </para>
 25
 26<sect2 id="macro-tips-input-single-line">
 27<title>Getting a Single Line of Text</title>
 28
 29  <para>
 30    As mentioned earlier in <xref linkend="helpful-methods" />, the method
 31    <function>Macros.input()</function> offers a convenient way to obtain
 32    a single line of text input. Here is an example that inserts a pair
 33    of HTML markup tags specified by the user.
 34  </para>
 35
 36<informalexample><programlisting>// Insert_Tag.bsh
 37
 38void insertTag()
 39{
 40    caret = textArea.getCaretPosition();
 41    tag = Macros.input(view, <quote>Enter name of tag:</quote>);
 42    if( tag == null || tag.length() == 0) return;
 43    text = textArea.getSelectedText();
 44    if(text == null) text = <quote></quote>;
 45    sb = new StringBuffer();
 46    sb.append(<quote>&lt;</quote>).append(tag).append(<quote>&gt;</quote>);
 47    sb.append(text);
 48    sb.append(<quote>&lt;/</quote>).append(tag).append(<quote>&gt;</quote>);
 49    textArea.setSelectedText(sb.toString());
 50    if(text.length() == 0)
 51        textArea.setCaretPosition(caret + tag.length() + 2);
 52}
 53
 54insertTag();
 55
 56// end Insert_Tag.bsh</programlisting></informalexample>
 57
 58  <para>
 59    Here the call to <function>Macros.input()</function> seeks the name
 60    of the markup tag. This method sets the message box title to a fixed string,
 61    <quote>Macro input</quote>, but the specific message <guilabel>Enter name
 62    of tag</guilabel> provides all the information necessary. The return value
 63    <varname>tag</varname> must be tested to see if it is null.  This would
 64    occur if the user presses the <guilabel>Cancel</guilabel> button or
 65    closes the dialog window displayed by <function>Macros.input()</function>.
 66  </para>
 67</sect2>
 68
 69<sect2 id="macro-tips-input-multiple-data"><title>Getting Multiple Data Items</title>
 70  <para>
 71    If more than one item of input is needed, a succession of calls to
 72    <function>Macros.input()</function> is a possible, but awkward approach,
 73    because it would not be possible to correct early input after the
 74    corresponding message box is dismissed.  Where more is required,
 75    but a full dialog layout is either unnecessary or too much work,
 76    the Java method <function>JOptionPane.showConfirmDialog()</function>
 77    is available. The version to use has the following prototype:
 78  </para>
 79
 80  <itemizedlist>
 81    <listitem>
 82      <funcsynopsis>
 83        <funcprototype>
 84          <funcdef>public static int <function>showConfirmDialog</function></funcdef>
 85          <paramdef>Component <parameter>parentComponent</parameter></paramdef>
 86          <paramdef>Object <parameter>message</parameter></paramdef>
 87          <paramdef>String <parameter>title</parameter></paramdef>
 88          <paramdef>int <parameter>optionType</parameter></paramdef>
 89          <paramdef>int <parameter>messageType</parameter></paramdef>
 90        </funcprototype>
 91      </funcsynopsis>
 92    </listitem>
 93  </itemizedlist>
 94
 95  <para>
 96    The usefulness of this method arises from the fact that the
 97    <varname>message</varname> parameter can be an object of any
 98    Java class (since all classes are derived from
 99    <classname>Object</classname>), or any array of objects.  The
100    following example shows how this feature can be used.
101  </para>
102
103  <informalexample><programlisting>// excerpt from Write_File_Header.bsh
104
105title = <quote>Write file header</quote>;
106
107currentName = buffer.getName();
108
109nameField = new JTextField(currentName);
110authorField = new JTextField(<quote>Your name here</quote>);
111descField = new JTextField(<quote></quote>, 25);
112
113namePanel = new JPanel(new GridLayout(1, 2));
114nameLabel = new JLabel(<quote>Name of file:</quote>, SwingConstants.LEFT);
115nameLabel.setForeground(Color.black);
116saveField = new JCheckBox(<quote>Save file when done</quote>,
117    !buffer.isNewFile());
118namePanel.add(nameLabel);
119namePanel.add(saveField);
120
121
122message = new Object[9];
123message[0] = namePanel;
124message[1] = nameField;
125message[2] = Box.createVerticalStrut(10);
126message[3] = <quote>Author's name:</quote>;
127message[4] = authorField;
128message[5] = Box.createVerticalStrut(10);
129message[6] = <quote>Enter description:</quote>;
130message[7] = descField;
131message[8] = Box.createVerticalStrut(5);
132
133if( JOptionPane.OK_OPTION !=
134    JOptionPane.showConfirmDialog(view, message, title,
135        JOptionPane.OK_CANCEL_OPTION,
136        JOptionPane.QUESTION_MESSAGE))
137    return null;
138
139// *****remainder of macro script omitted*****
140
141// end excerpt from Write_File_Header.bsh</programlisting></informalexample>
142
143  <para>
144    This macro takes several items of user input and produces a formatted
145    file header at the beginning of the buffer. The full macro is included in
146    the set of macros installed by jEdit. There are a number of input
147    features of this excerpt worth noting.
148  </para>
149
150  <itemizedlist>
151    <listitem>
152      <para>
153        The macro uses a total of seven visible components. Two of them are
154        created behind the scenes by <function>showConfirmDialog()</function>,
155        the rest are made by the macro. To arrange them, the script creates an
156        array of <classname>Object</classname> objects and assigns components to
157        each location in the array. This translates to a fixed, top-to-bottom
158        arrangement in the message box created by
159        <function>showConfirmDialog()</function>.
160      </para>
161    </listitem>
162
163    <listitem>
164      <para>
165        The macro uses <classname>JTextField</classname> objects to
166        obtain most of the input data.  The fields <varname>nameField</varname>
167        and <varname>authorField</varname> are created with constructors
168        that take the initial, default text to be displayed in the field as a
169        parameter.  When the message box is displayed, the default text
170        will appear and can be altered or deleted by the user.
171      </para>
172    </listitem>
173
174    <listitem>
175      <para>
176        The text field <varname>descField</varname> uses an empty string for its
177        initial value. The second parameter in its constructor sets the width of
178        the text field component, expressed as the number of characters of
179        <quote>average</quote> width. When
180        <function>showConfirmDialog()</function> prepares the layout of the
181        message box, it sets the width wide enough to accommodate the
182        designated with of <varname>descField</varname>. This technique produces
183        a message box and input text fields that are wide enough for your data
184        with one line of code.
185      </para>
186    </listitem>
187
188    <listitem>
189      <para>
190        The displayed message box includes a <classname>JCheckBox</classname>
191        component that determines whether the buffer will be saved to disk
192        immediately after the file header is written. To conserve space
193        in the message box, we want to display the check box to the
194        right of the label <guilabel>Name of file:</guilabel>. To do that,
195        we create a <classname>JPanel</classname> object and populate it with
196        the label and the checkbox in a left-to-right
197        <classname>GridLayout</classname>.  The <classname>JPanel</classname>
198        containing the two components is then added to the beginning of
199        <varname>message</varname> array.
200      </para>
201    </listitem>
202
203    <listitem>
204      <para>
205        The two visible components created by
206        <function>showConfirmDialog()</function> appear at positions 3 and 6 of
207        the <varname>message</varname> array. Only the text is required; they
208        are rendered as text labels. Note that the constructor sets the
209        foreground color <varname>nameLabel</varname> to black. The default text
210        color of <classname>JLabel</classname> objects is gray for Java's
211        default look-and-feel, so the color was reset for consistency with
212        the rest of the message box.
213      </para>
214    </listitem>
215
216    <listitem>
217      <para>
218        There are three invisible components created by
219        <function>showConfirmDialog()</function>.  Each of them involves
220        a call to <function>Box.createVerticalStrut()</function>.  The
221        <classname>Box</classname> class is a sophisticated layout class
222        that gives the user great flexibility in sizing and positioning
223        components. Here we use a <function>static</function> method of
224        the <classname>Box</classname> class that produces a vertical
225        <glossterm>struct</glossterm>.  This is a transparent component
226        whose width expands to fill its parent component (in this case,
227        the message box).  The single parameter indicates the fixed height
228        of the spacing <quote>strut</quote> in pixels.  The last call to
229        <function>createVerticalStrut()</function> separates the
230        description text field from the <guilabel>OK</guilabel> and
231        <guilabel>Cancel</guilabel> buttons that are automatically added
232        by <function>showConfirmDialog()</function>.
233      </para>
234    </listitem>
235
236    <listitem>
237      <para>
238        Finally, the call to <function>showConfirmDialog()</function> uses
239        defined constants for the option type and the message type.  The
240        constants are the same as those used with the
241        <function>Macros.confirm()</function> method; see
242        <xref linkend="helpful-methods" />. The
243        option type signifies the use of <guilabel>OK</guilabel>
244        and <guilabel>Cancel</guilabel> buttons. The
245        <constant>QUERY_MESSAGE</constant> message type causes the message box
246        to display a question mark icon.
247      </para>
248      <para>
249        The return value of the method
250        is tested against the value <constant>OK_OPTION</constant>.  If
251        the return value is something else (because the
252        <guilabel>Cancel</guilabel> button was pressed or because the
253        message box window was closed without a button press), a
254        <constant>null</constant> value is returned to a calling function,
255        signaling that the user canceled macro execution.  If the return
256        value is <constant>OK_OPTION</constant>, each of the input components
257        can yield their contents for further processing by calls to
258        <function>JTextField.getText()</function> (or, in the case of
259        the check box, <function>JCheckBox.isSelected()</function>).
260      </para>
261    </listitem>
262
263  </itemizedlist>
264</sect2>
265
266<sect2 id="tips-macro-input-combo"><title>Selecting Input From a List</title>
267<para>
268  Another useful way to get user input for a macro is to use a combo box
269  containing a number of pre-set options. If this is the only input
270  required, one of the versions of <function>showInputDialog()</function>
271  in the <classname>JOptionPane</classname> class provides a shortcut.
272  Here is its prototype:
273</para>
274
275  <itemizedlist>
276    <listitem>
277      <funcsynopsis>
278        <funcprototype>
279          <funcdef>public static Object <function>showInputDialog</function></funcdef>
280          <paramdef>Component <parameter>parentComponent</parameter></paramdef>
281          <paramdef>Object <parameter>message</parameter></paramdef>
282          <paramdef>String <parameter>title</parameter></paramdef>
283          <paramdef>int <parameter>messageType</parameter></paramdef>
284          <paramdef>Icon <parameter>icon</parameter></paramdef>
285          <paramdef>Object[] <parameter>selectionValues</parameter></paramdef>
286          <paramdef>Object <parameter>initialSelectionValue</parameter></paramdef>
287        </funcprototype>
288      </funcsynopsis>
289    </listitem>
290  </itemizedlist>
291
292  <para>
293    This method creates a message box containing a drop-down list of the
294    options specified in the method's parameters, along with
295    <guilabel>OK</guilabel> and <guilabel>Cancel</guilabel> buttons.
296    Compared to <function>showConfirmDialog()</function>, this method lacks
297    an <varname>optionType</varname> parameter and has three additional
298    parameters: an <varname>icon</varname> to display in the dialog
299    (which can be set to <constant>null</constant>), an array of
300    <varname>selectionValues</varname> objects, and a reference to one
301    of the options as the <varname>initialSelectionValue</varname> to be
302    displayed. In addition, instead of returning an <classname>int</classname>
303    representing the user's action, <function>showInputDialog()</function>
304    returns the <classname>Object</classname> corresponding to the user's
305    selection, or <constant>null</constant> if the selection is canceled.
306  </para>
307
308  <para>
309    The following macro fragment illustrates the use of this method.
310  </para>
311
312  <informalexample>
313  <programlisting>// fragment illustrating use of showInputDialog()
314options = new Object[5];
315options[0] = "JLabel";
316options[1] = "JTextField";
317options[2] = "JCheckBox";
318options[3] = "HistoryTextField";
319options[4} = "-- other --";
320
321result = JOptionPane.showInputDialog(view,
322    "Choose component class",
323    "Select class for input component",
324    JOptionPane.QUESTION_MESSAGE,
325    null, options, options[0]);</programlisting>
326  </informalexample>
327
328  <para>
329    The return value <varname>result</varname> will contain either the
330    <classname>String</classname> object representing the selected text
331    item or <constant>null</constant> representing no selection.  Any
332    further use of this fragment would have to test the value of
333    <varname>result</varname> and likely exit from the macro if the value
334    equaled <constant>null</constant>.
335  </para>
336
337  <para>
338    A set of options can be similarly placed in a
339    <classname>JComboBox</classname> component created as part of a larger
340    dialog or <function>showMessageDialog()</function> layout.  Here are some
341    code fragments showing this approach:
342  </para>
343
344  <informalexample>
345  <programlisting>// fragments from Display_Abbreviations.bsh
346// import statements and other code omitted
347
348// from main routine, this method call returns an array
349// of Strings representing the names of abbreviation sets
350
351abbrevSets = getActiveSets();
352
353...
354
355// from showAbbrevs() method
356
357combo = new JComboBox(abbrevSets);
358// set width to uniform size regardless of combobox contents
359Dimension dim = combo.getPreferredSize();
360dim.width = Math.max(dim.width, 120);
361combo.setPreferredSize(dim);
362combo.setSelectedItem(STARTING_SET); // defined as "global"
363
364// end fragments</programlisting>
365  </informalexample>
366
367</sect2>
368
369
370<sect2 id="macro-tips-single-char"><title>Using a Single Keypress as Input</title>
371
372  <para>
373    Some macros may choose to emulate the style of character-based text
374    editors such as <application>emacs</application> or
375    <application>vi</application>. They will require only a single keypress
376    as input that would be handled by the macro but not displayed on the
377    screen. If the keypress corresponds to a character value, jEdit can pass
378    that value as a parameter to a BeanShell script.
379  </para>
380
381  <para>
382    The jEdit class <classname>InputHandler</classname> is an abstract class
383    that that manages associations between keyboard input and editing
384    actions, along with the recording of macros. Keyboard input
385    in jEdit is normally managed by the derived class
386    <classname>DefaultInputHandler</classname>. One of the methods in
387    the <classname>InputHandler</classname> class handles input from a
388    single keypress:
389  </para>
390
391  <itemizedlist>
392    <listitem>
393      <funcsynopsis>
394        <funcprototype>
395          <funcdef>public void <function>readNextChar</function></funcdef>
396          <paramdef>String <parameter>prompt</parameter></paramdef>
397          <paramdef>String <parameter>code</parameter></paramdef>
398        </funcprototype>
399      </funcsynopsis>
400    </listitem>
401  </itemizedlist>
402
403  <para>
404    When this method is called, the contents of the <varname>prompt</varname>
405    parameter is shown in the view's status
406    bar. The method then waits for a key press, after which
407    the contents of the <varname>code</varname> parameter will be run as
408    a BeanShell script, with one important modification. Each time the
409    string <varname>__char__</varname> appears in the parameter script, it
410    will be substituted by the character pressed. The key press
411    is <quote>consumed</quote> by <function>readNextChar()</function>.
412    It will not be displayed on the screen or otherwise processed by jEdit.
413  </para>
414
415  <para>
416    Using <function>readNextChar()</function> requires a macro within the
417    macro, formatted as a single, potentially lengthy string literal. The
418    following macro illustrates this technique. It selects a line of text
419    from the current caret position to the first occurrence of the character
420    next typed by the user. If the character does not appear on the line, no
421    new selection occurs and the display remains unchanged.
422  </para>
423
424  <informalexample>
425  <programlisting>// Next_Char.bsh
426
427script = new StringBuffer(512);
428script.append( "start = textArea.getCaretPosition();"         );
429script.append( "line = textArea.getCaretLine();"              );
430script.append( "end = textArea.getLineEndOffset(line) + 1;"   );
431script.append( "text = buffer.getText(start, end - start);"   );
432script.append( "match = text.indexOf(__char__, 1);"           );
433script.append( "if(match != -1) {"                            );
434script.append(   "if(__char__ != '\\n') ++match;"             );
435script.append(   "textArea.select(start, start + match - 1);" );
436script.append( "}"                                            );
437
438view.getInputHandler().readNextChar("Enter a character",
439    script.toString());
440
441// end Next_Char.bsh</programlisting>
442  </informalexample>
443
444  <para>
445    Once again, here are a few comments on the macro's design.
446  </para>
447
448  <itemizedlist>
449    <listitem>
450      <para>
451        A <classname>StringBuffer</classname> object is used for efficiency; it
452        obviates multiple creation of fixed-length <classname>String</classname>
453        objects. The parameter to the constructor of <varname>script</varname>
454        specifies the initial size of the buffer that will receive the contents
455        of the child script.
456      </para>
457    </listitem>
458
459    <listitem>
460      <para>
461        Besides the quoting of the script code, the formatting of the macro is
462        entirely optional but (hopefully) makes it easier to read.
463      </para>
464    </listitem>
465
466    <listitem>
467      <para>
468        It is important that the child script be self-contained.  It does
469        not run in the same namespace as the <quote>parent</quote> macro
470        <filename>Next_Char.bsh</filename> and therefore does not share
471        variables, methods, or scripted objects defined in the parent
472        macro.
473      </para>
474    </listitem>
475
476    <listitem>
477      <para>
478        Finally, access to the <classname>InputHandler</classname> object used
479        by jEdit is available by calling
480        <function>getInputHandler()</function> on the current view.
481      </para>
482    </listitem>
483
484  </itemizedlist>
485
486</sect2>
487
488</sect1>
489
490
491<sect1 id="startup-scripts"><title>Startup Scripts</title>
492
493  <para>
494    On startup, jEdit runs any BeanShell scripts located in the
495    <filename>startup</filename> subdirectory of the jEdit installation and
496    user settings directories (see <xref linkend="settings-directory" />). As
497    with macros, the scripts must have a <filename>.bsh</filename> file name
498    extension. Startup scripts are run near the end of the startup sequence,
499    after plugins, properties and such have been initialized, but before the
500    first view is opened.
501  </para>
502
503  <para>
504    Startup scripts can perform initialization tasks that cannot be handled
505    by command line options or ordinary configuration options, such as
506    customizing jEdit's user interface by
507    changing entries in the Java platform's
508    <classname>UIManager</classname> class.
509  </para>
510
511  <para>
512    Startup scripts have an additional feature lacking in ordinary macros
513    that can help you further customize jEdit. Variables and methods
514    defined in a startup script are available in all instances of the BeanShell
515    interpreter created in jEdit. This allows you to create a personal
516    library of methods and objects that can be accessed at any time during
517    the editing session in another macro, the BeanShell shell of the Console
518    plugin, or menu items such as
519    <guimenu>Utilities</guimenu>&gt;<guisubmenu>BeanShell</guisubmenu>&gt;<guimenuitem>Evaluate
520    BeanShell Expression</guimenuitem>.
521  </para>
522
523  <para>
524    The startup script routine will run script files in the installation
525    directory first, followed by scripts in the user settings directory. In
526    each case, scripts will be executed in alphabetical order, applied
527    without regard to whether the file name contains upper or lower case
528    characters.
529  </para>
530
531  <para>
532    If a startup script throws an exception (because, for example,
533    it attempts to call a method on a <constant>null</constant> object).
534    jEdit will show an error dialog box and move on to the next
535    startup script. If script bugs are causing jEdit to crash or
536    hang on startup, you can use the
537    <command>-nostartupscripts</command> command line option to
538    disable them for that editing session.
539  </para>
540
541  <para>
542    Another important difference between startup scripts and ordinary
543    macros is that startup scripts cannot use the pre-defined variables
544    <varname>view</varname>, <varname>textArea</varname>,
545    <varname>editPane</varname> and <varname>buffer</varname>. This is
546    because they are executed before the initial view is created.
547  </para>
548
549  <para>
550    If you are writing a method in a
551    startup script and wish to use one of the above variables, pass
552    parameters of the
553    appropriate type to the method, so that a macro calling them after
554    startup can supply the appropriate values. For example, a startup script
555    could include a method
556  </para>
557
558  <informalexample><programlisting>void doSomethingWithView(View v, String s)  {
559    ...
560}</programlisting></informalexample>
561
562  <para>
563    so that during the editing session another macro can call the method
564    using
565  </para>
566
567  <informalexample><programlisting>doSomethingWithView(view, "something");</programlisting></informalexample>
568
569  <sidebar><title>Reloading startup scripts without restarting</title>
570   <para>
571    It is actually possible to reload startup scripts or load other scripts
572    without restarting
573    jEdit, using a BeanShell statement like the following:
574   </para>
575   <programlisting>BeanShell.runScript(view,<replaceable>path</replaceable>,false,false);</programlisting>
576   <para>
577    For <replaceable>path</replaceable>, you can substitute any string,
578    or a method call such as <function>buffer.getPath()</function>.
579   </para>
580  </sidebar>
581</sect1>
582
583<sect1 id="scripts-command-line"><title>Running Scripts from the Command
584Line</title>
585
586<para>
587  The <command>-run</command> command line switch specifies a BeanShell
588  script to run on startup:
589</para>
590
591<screen><prompt>$ </prompt><userinput>jedit -run=test.bsh</userinput></screen>
592
593<para>
594  Note that just like with startup scripts, the <varname>view</varname>,
595  <varname>textArea</varname>, <varname>editPane</varname> and
596  <varname>buffer</varname> variables are not defined.
597</para>
598
599<para>
600  If another instance is already running, the script will be run in that
601  instance, and you will be able to use the
602  <function>jEdit.getLastView()</function> method to obtain a view.
603  However, if a new instance of jEdit is being started, the script will
604  be run at the same time as all other startup scripts; that is, before
605  the first view is opened.
606</para>
607
608<para>
609  If your script needs a view instance to operate on, you can use the
610  following code pattern to obtain one, no matter how or when the script is
611  being run:
612</para>
613
614<programlisting>void doSomethingUseful()
615{
616    void run()
617    {
618        view = jEdit.getLastView();
619
620        // put actual script body here
621    }
622
623    if(jEdit.getLastView() == null)
624        VFSManager.runInAWTThread(this);
625    else
626        run();
627}
628
629doSomethingUseful();</programlisting>
630
631<para>
632  If the script is being run in a loaded
633  instance, it can be invoked to perform its work immediately.
634  However, if
635  the script is running at startup, before an initial view exists, its
636  operation must be delayed to allow the view object first to be created
637  and displayed.
638  In order to queue the macro's operation, the scripted <quote>closure</quote>
639  named <function>doSomethingUseful()</function> implements the
640  <classname>Runnable</classname> interface of the Java platform.
641  That interface contains only
642  a single <function>run()</function> method that takes no parameters
643  and has no return value. The macro's implementation of the
644  <function>run()</function> method contains the <quote>working</quote>
645  portion of the macro.  Then the scripted object, represented by a
646  reference to <varname>this</varname>, is passed to the
647  <function>runInAWTThread()</function> method. This schedules the
648  macro's operations for execution after the startup routine is complete.
649</para>
650
651<para>
652  As this example illustrates, the <function>runInAWTThread()</function>
653  method can be used to ensure that a macro will perform operations
654  after other operations have
655  completed. If it is invoked during startup, it schedules the specified
656  <classname>Runnable</classname> object to run after startup is complete.
657  If invoked when jEdit is fully loaded, the <classname>Runnable</classname>
658  object will execute after
659  all pending input/output is complete, or immediately if there are no
660  pending I/O operations. This will delay operations on a new buffer,
661  for example, until after the buffer is loaded and displayed.
662</para>
663
664</sect1>
665
666<sect1 id="macro-tips-BeanShell"><title>Advanced BeanShell Techniques</title>
667
668<para>
669  BeanShell has a few advanced features that we haven't mentioned yet.
670  They will be discussed in this section.
671</para>
672
673<sect2 id="macro-tips-BeanShell-convenience">
674<title>BeanShell's Convenience Syntax</title>
675
676<para>
677  We noted earlier that BeanShell syntax does not require that variables
678  be declared or defined with their type, and that variables that are
679  not typed when first used can have values of differing types assigned
680  to them. In addition to this <quote>loose</quote> syntax, BeanShell
681  allows a <quote>convenience</quote> syntax for dealing with the
682  properties of JavaBeans.  They may be accessed or set as if they were
683  data members. They may also be accessed using the name of the property
684  enclosed in quotation marks and curly brackets.  For example, the following
685  statement are all equivalent, assuming <varname>btn</varname> is
686  a <classname>JButton</classname> instance:
687</para>
688
689<informalexample><programlisting>b.setText("Choose");
690b.text = "Choose";
691b{"text"} = "Choose";
692</programlisting></informalexample>
693
694<para>
695  The last form can also be used to access a key-value pair of a
696  <classname>Hashtable</classname> object. It can even be used to obtain
697  the values of buffer-local properties; the following two statements
698  are equivalent:
699</para>
700
701<informalexample><programlisting>buffer.getProperty("tabSize")
702buffer{"tabSize"}
703</programlisting></informalexample>
704
705<!-- actually, the following requires the bsh.classpath package, which
706     is not included with jEdit at this point in time.
707
708     a future release of jEdit will use bsh.classpath, and hence support
709     'import *'
710
711<para>
712  Finally, when importing classes, BeanShell permits the following form
713  to import all classes lying within the interpreter's classpath:
714</para>
715
716<informalexample><programlisting>import *;
717</programlisting></informalexample>
718
719-->
720
721</sect2>
722
723
724<sect2 id="macro-tips-BeanShell-keywords">
725<title>Special BeanShell Keywords</title>
726
727<para>
728  BeanShell uses special keywords to refer to variables or methods defined in
729  the current or an enclosing block's scope:
730</para>
731
732<itemizedlist>
733  <listitem>
734    <para>
735      The keyword <function>this</function> refers to the current scope.
736    </para>
737  </listitem>
738  <listitem>
739    <para>
740      The keyword <function>super</function> refers to the immediately
741      enclosing scope.
742    </para>
743  </listitem>
744  <listitem>
745    <para>
746      The keyword <function>global</function> refers to the top-level
747      scope of the macro script.
748    </para>
749  </listitem>
750</itemizedlist>
751
752<para>
753  The following script illustrates the use of these keywords:
754</para>
755
756<informalexample><programlisting>a = "top\n";
757foo() {
758    a = "middle\n";
759    bar() {
760        a = "bottom\n";
761        textArea.setSelectedText(global.a);
762        textArea.setSelectedText(super.a);
763        // equivalent to textArea.setSelectedText(this.a):
764        textArea.setSelectedText(a);
765    }
766
767    bar();
768}
769foo();</programlisting></informalexample>
770
771<para>
772  When the script is run, the following text is inserted in the current
773  buffer:
774</para>
775
776<screen>top
777middle
778bottom</screen>
779
780</sect2>
781
782<sect2 id="macro-tips-BeanShell-interface">
783<title>Implementing Interfaces</title>
784
785<para>
786  As discussed in the macro example in <xref
787  linkend="dialog-macro"/>, scripted objects can implement Java
788  interfaces such as <classname>ActionListener</classname>.  Which
789  interfaces may be implemented varies depending upon the
790  version of the Java runtime environment being used. If running under
791  Java 1.1 or 1.2, BeanShell objects can only implement the AWT or
792  Swing event listener interfaces contained in the
793  <literal>java.awt.event</literal> and <literal>javax.swing.event</literal>
794  packages, along with the <classname>java.lang.Runnable</classname> interface.
795  If BeanShell is running under Java 1.3 or 1.4, which jEdit 4.0 requires, any
796  interface can be implemented.
797</para>
798
799<para>
800  Frequently it will not be necessary to implement all of the methods of
801  a particular interface in order to specify the behavior of a scripted
802  object. Under Java 1.3 and above, the virtual machine's reflection
803  mechanism will throw an exception for any
804  missing interface methods.  This will bring macro execution to a
805  halt unless the exception is trapped and handled.  The better solution
806  is to implement the <function>invoke()</function> method,
807  which is called when an undefined method is invoked on a scripted
808  object. Typically, the implementation of this method will do nothing,
809  as in the following example:
810</para>
811
812<informalexample><programlisting>invoke(method, args) {}</programlisting>
813</informalexample>
814
815</sect2>
816
817<sect2 id="macro-tips-beanshell-commands"><title>BeanShell Commands</title>
818
819<para>
820  BeanShell comes with a large number of built-in scripted
821  <quote>commands</quote> that are useful in many circumstances.
822  Documentation for commands that are helpful when
823  writing macros can be found in <xref linkend="beanshell-commands"/>.
824</para>
825
826</sect2>
827
828</sect1>
829
830<sect1 id="macro-tips-debugging"><title>Debugging Macros</title>
831
832  <para>
833    Here are a few techniques that can prove helpful in debugging
834    macros.
835  </para>
836
837<sect2 id="macro-tips-debugging-exceptions">
838<title>Identifying Exceptions</title>
839
840  <para>
841    An <glossterm>exception</glossterm> is a condition reflecting an error
842    or other unusual result of program execution that requires interruption
843    of normal program flow and some kind of special handling. Java has a
844    rich (and extensible) collection of exception classes which represent
845    such conditions.
846  </para>
847
848  <para>
849    jEdit catches exceptions thrown by BeanShell scripts and displays
850    them in a dialog box. In addition, the full traceback is written to
851    the activity log (see <xref linkend="activity-log"/> for more
852    information about the activity log).
853  </para>
854
855  <para>
856    There are two broad categories of errors that will result in
857    exceptions:
858  </para>
859
860  <itemizedlist>
861   <listitem>
862    <para>
863     <emphasis>Interpreter errors</emphasis>, which may arise from typing
864     mistakes like mismatched brackets or missing semicolons, or from
865     BeanShell's failure to find a class corresponding to a particular
866     variable.
867    </para>
868    <para>
869     Interpreter errors are usually accompanied by the line number in
870     the script, along with the cause of the error.
871    </para>
872   </listitem>
873   <listitem>
874    <para>
875     <emphasis>Execution errors</emphasis>, which result from
876     runtime exceptions thrown by the Java platform when macro code is
877     executed.
878    </para>
879    <para>
880     Some exceptions thrown by the Java platform can often seem
881     cryptic. Nevertheless, examining the
882     contents of the activity log may reveals clues as to the cause of
883     the error.
884    </para>
885   </listitem>
886  </itemizedlist>
887
888</sect2>
889
890<sect2 id="macro-tips-debugging-log">
891<title>Using the Activity Log as a Tracing Tool</title>
892
893  <para>
894    Sometimes exception tracebacks will say
895    what kind of error occurred but not where it arose in the script.
896    In those cases, you can insert calls that log messages to the
897    activity log in your macro. If the logged messages appear when the
898    macro is run, it means that up to that point the macro is fine;
899    but if an exception is logged first, it means the logging call is
900    located after the cause of the error.
901  </para>
902
903  <para>
904   To write a message to the activity log,
905   use the following method of the <classname>Log</classname>
906   class:
907  </para>
908
909  <itemizedlist>
910    <listitem>
911      <funcsynopsis>
912        <funcprototype>
913          <funcdef>public static void <function>log</function></funcdef>
914          <paramdef>int <parameter>urgency</parameter></paramdef>
915          <paramdef>Object <parameter>source</parameter></paramdef>
916          <paramdef>Object <parameter>message</parameter></paramdef>
917        </funcprototype>
918      </funcsynopsis>
919    </listitem>
920  </itemizedlist>
921
922  <para>
923   The parameter <varname>urgency</varname> can take one of the
924   following constant values:
925  </para>
926
927  <itemizedlist>
928   <listitem><para><constant>Log.DEBUG</constant></para></listitem>
929   <listitem><para><constant>Log.MESSAGE</constant></para></listitem>
930   <listitem><para><constant>Log.NOTICE</constant></para></listitem>
931   <listitem><para><constant>Log.WARNING</constant></para></listitem>
932   <listitem><para><constant>Log.ERROR</constant></para></listitem>
933  </itemizedlist>
934
935  <para>
936   Note that the <varname>urgency</varname> parameter merely changes
937   the string prefixed to the log message; it does not change the logging
938   behavior in any other way.
939  </para>
940
941  <para>
942   The parameter <varname>source</varname> can be either an object or a
943   class instance. When writing log messages from macros, set
944   this parameter to <literal>BeanShell.class</literal> to make macro
945   errors easier to spot in the activity log.
946  </para>
947
948  <para>
949    The following code sends a typical debugging message to the
950    activity log:
951  </para>
952
953    <informalexample><programlisting>Log.log(Log.DEBUG, BeanShell.class,
954    "counter = " + String.valueOf(counter));</programlisting></informalexample>
955
956  <para>
957    The corresponding activity log entry might read as follows:
958  </para>
959
960    <informalexample>
961    <programlisting>[debug] BeanShell: counter = 15</programlisting>
962    </informalexample>
963
964  <sidebar><title>Using message dialog boxes as a tracing tool</title>
965    <para>
966      If you would prefer not having to deal with the activity log, you
967      can use the <function>Macros.message()</function> method as a
968      tracing tool. Just insert calls like the following in the macro
969      code:
970    </para>
971
972    <programlisting>Macros.message(view,"tracing");</programlisting>
973
974    <para>
975      Execution of the macro is halted until the message dialog box is
976      closed. When you have finished debugging the macro, you should
977      delete or comment out the debugging calls to
978      <function>Macros.message()</function> in your final source code.
979    </para>
980  </sidebar>
981
982</sect2>
983
984</sect1>
985
986</chapter>