PageRenderTime 547ms CodeModel.GetById 535ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

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

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