PageRenderTime 113ms CodeModel.GetById 50ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 16ms

/www/tags/NOV_07_2009/htdocs/42docs/users-guide/macro-tips.xml

#
XML | 961 lines | 789 code | 147 blank | 25 comment | 0 complexity | 2cd4ad93546d3120b0eb26341dc87c65 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  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<!-- :xml.root=users-guide.xml:                               -->
  7<!--                                                          -->
  8<!-- This file covers the chapter "Macro tips and techniques" -->
  9<!-- $Id: macro-tips.xml,v 1.18 2004/03/17 05:15:52 spestov Exp $
 10 -->
 11
 12<chapter id="macro-tips"><title>Macro Tips and Techniques</title>
 13
 14<sect1 id="macro-tips-input">
 15<title>Getting Input for a Macro</title>
 16
 17  <para>
 18    The dialog-based macro discussed in <xref linkend="dialog-macro"/>
 19    reflects a conventional approach to obtaining input in a Java program.
 20    Nevertheless, it can be too lengthy or tedious for someone
 21    trying to write a macro quickly. Not every macro needs a user interface
 22    specified in such detail; some macros require only a single keystroke or
 23    no input at all. In this section we outline some other techniques for
 24    obtaining input that will help you write macros quickly.
 25  </para>
 26
 27<sect2 id="macro-tips-input-single-line">
 28<title>Getting a Single Line of Text</title>
 29
 30  <para>
 31    As mentioned earlier in <xref linkend="helpful-methods" />, the method
 32    <function>Macros.input()</function> offers a convenient way to obtain
 33    a single line of text input. Here is an example that inserts a pair
 34    of HTML markup tags specified by the user.
 35  </para>
 36
 37<informalexample><programlisting>// Insert_Tag.bsh
 38
 39void insertTag()
 40{
 41    caret = textArea.getCaretPosition();
 42    tag = Macros.input(view, <quote>Enter name of tag:</quote>);
 43    if( tag == null || tag.length() == 0) return;
 44    text = textArea.getSelectedText();
 45    if(text == null) text = <quote></quote>;
 46    sb = new StringBuffer();
 47    sb.append(<quote>&lt;</quote>).append(tag).append(<quote>&gt;</quote>);
 48    sb.append(text);
 49    sb.append(<quote>&lt;/</quote>).append(tag).append(<quote>&gt;</quote>);
 50    textArea.setSelectedText(sb.toString());
 51    if(text.length() == 0)
 52        textArea.setCaretPosition(caret + tag.length() + 2);
 53}
 54
 55insertTag();
 56
 57// end Insert_Tag.bsh</programlisting></informalexample>
 58
 59  <para>
 60    Here the call to <function>Macros.input()</function> seeks the name
 61    of the markup tag. This method sets the message box title to a fixed string,
 62    <quote>Macro input</quote>, but the specific message <guilabel>Enter name
 63    of tag</guilabel> provides all the information necessary. The return value
 64    <varname>tag</varname> must be tested to see if it is null.  This would
 65    occur if the user presses the <guilabel>Cancel</guilabel> button or
 66    closes the dialog window displayed by <function>Macros.input()</function>.
 67  </para>
 68</sect2>
 69
 70<sect2 id="macro-tips-input-multiple-data"><title>Getting Multiple Data Items</title>
 71  <para>
 72    If more than one item of input is needed, a succession of calls to
 73    <function>Macros.input()</function> is a possible, but awkward approach,
 74    because it would not be possible to correct early input after the
 75    corresponding message box is dismissed.  Where more is required,
 76    but a full dialog layout is either unnecessary or too much work,
 77    the Java method <function>JOptionPane.showConfirmDialog()</function>
 78    is available. The version to use has the following prototype:
 79  </para>
 80
 81  <itemizedlist>
 82    <listitem>
 83      <funcsynopsis>
 84        <funcprototype>
 85          <funcdef>public static int <function>showConfirmDialog</function></funcdef>
 86          <paramdef>Component <parameter>parentComponent</parameter></paramdef>
 87          <paramdef>Object <parameter>message</parameter></paramdef>
 88          <paramdef>String <parameter>title</parameter></paramdef>
 89          <paramdef>int <parameter>optionType</parameter></paramdef>
 90          <paramdef>int <parameter>messageType</parameter></paramdef>
 91        </funcprototype>
 92      </funcsynopsis>
 93    </listitem>
 94  </itemizedlist>
 95
 96  <para>
 97    The usefulness of this method arises from the fact that the
 98    <varname>message</varname> parameter can be an object of any
 99    Java class (since all classes are derived from
100    <classname>Object</classname>), or any array of objects.  The
101    following example shows how this feature can be used.
102  </para>
103
104  <informalexample><programlisting>// excerpt from Write_File_Header.bsh
105
106title = <quote>Write file header</quote>;
107
108currentName = buffer.getName();
109
110nameField = new JTextField(currentName);
111authorField = new JTextField(<quote>Your name here</quote>);
112descField = new JTextField(<quote></quote>, 25);
113
114namePanel = new JPanel(new GridLayout(1, 2));
115nameLabel = new JLabel(<quote>Name of file:</quote>, SwingConstants.LEFT);
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.
209      </para>
210    </listitem>
211
212    <listitem>
213      <para>
214        There are three invisible components created by
215        <function>showConfirmDialog()</function>.  Each of them involves
216        a call to <function>Box.createVerticalStrut()</function>.  The
217        <classname>Box</classname> class is a sophisticated layout class
218        that gives the user great flexibility in sizing and positioning
219        components. Here we use a <function>static</function> method of
220        the <classname>Box</classname> class that produces a vertical
221        <glossterm>strut</glossterm>.  This is a transparent component
222        whose width expands to fill its parent component (in this case,
223        the message box).  The single parameter indicates the height
224        of the strut in pixels.  The last call to
225        <function>createVerticalStrut()</function> separates the
226        description text field from the <guilabel>OK</guilabel> and
227        <guilabel>Cancel</guilabel> buttons that are automatically added
228        by <function>showConfirmDialog()</function>.
229      </para>
230    </listitem>
231
232    <listitem>
233      <para>
234        Finally, the call to <function>showConfirmDialog()</function> uses
235        defined constants for the option type and the message type.  The
236        constants are the same as those used with the
237        <function>Macros.confirm()</function> method; see
238        <xref linkend="helpful-methods" />. The
239        option type signifies the use of <guilabel>OK</guilabel>
240        and <guilabel>Cancel</guilabel> buttons. The
241        <constant>QUERY_MESSAGE</constant> message type causes the message box
242        to display a question mark icon.
243      </para>
244      <para>
245        The return value of the method
246        is tested against the value <constant>OK_OPTION</constant>.  If
247        the return value is something else (because the
248        <guilabel>Cancel</guilabel> button was pressed or because the
249        message box window was closed without a button press), a
250        <constant>null</constant> value is returned to a calling function,
251        signaling that the user canceled macro execution.  If the return
252        value is <constant>OK_OPTION</constant>, each of the input components
253        can yield their contents for further processing by calls to
254        <function>JTextField.getText()</function> (or, in the case of
255        the check box, <function>JCheckBox.isSelected()</function>).
256      </para>
257    </listitem>
258
259  </itemizedlist>
260</sect2>
261
262<sect2 id="tips-macro-input-combo"><title>Selecting Input From a List</title>
263<para>
264  Another useful way to get user input for a macro is to use a combo box
265  containing a number of pre-set options. If this is the only input
266  required, one of the versions of <function>showInputDialog()</function>
267  in the <classname>JOptionPane</classname> class provides a shortcut.
268  Here is its prototype:
269</para>
270
271  <itemizedlist>
272    <listitem>
273      <funcsynopsis>
274        <funcprototype>
275          <funcdef>public static Object <function>showInputDialog</function></funcdef>
276          <paramdef>Component <parameter>parentComponent</parameter></paramdef>
277          <paramdef>Object <parameter>message</parameter></paramdef>
278          <paramdef>String <parameter>title</parameter></paramdef>
279          <paramdef>int <parameter>messageType</parameter></paramdef>
280          <paramdef>Icon <parameter>icon</parameter></paramdef>
281          <paramdef>Object[] <parameter>selectionValues</parameter></paramdef>
282          <paramdef>Object <parameter>initialSelectionValue</parameter></paramdef>
283        </funcprototype>
284      </funcsynopsis>
285    </listitem>
286  </itemizedlist>
287
288  <para>
289    This method creates a message box containing a drop-down list of the
290    options specified in the method's parameters, along with
291    <guilabel>OK</guilabel> and <guilabel>Cancel</guilabel> buttons.
292    Compared to <function>showConfirmDialog()</function>, this method lacks
293    an <varname>optionType</varname> parameter and has three additional
294    parameters: an <varname>icon</varname> to display in the dialog
295    (which can be set to <constant>null</constant>), an array of
296    <varname>selectionValues</varname> objects, and a reference to one
297    of the options as the <varname>initialSelectionValue</varname> to be
298    displayed. In addition, instead of returning an <classname>int</classname>
299    representing the user's action, <function>showInputDialog()</function>
300    returns the <classname>Object</classname> corresponding to the user's
301    selection, or <constant>null</constant> if the selection is canceled.
302  </para>
303
304  <para>
305    The following macro fragment illustrates the use of this method.
306  </para>
307
308  <informalexample>
309  <programlisting>// fragment illustrating use of showInputDialog()
310options = new Object[5];
311options[0] = "JLabel";
312options[1] = "JTextField";
313options[2] = "JCheckBox";
314options[3] = "HistoryTextField";
315options[4} = "-- other --";
316
317result = JOptionPane.showInputDialog(view,
318    "Choose component class",
319    "Select class for input component",
320    JOptionPane.QUESTION_MESSAGE,
321    null, options, options[0]);</programlisting>
322  </informalexample>
323
324  <para>
325    The return value <varname>result</varname> will contain either the
326    <classname>String</classname> object representing the selected text
327    item or <constant>null</constant> representing no selection.  Any
328    further use of this fragment would have to test the value of
329    <varname>result</varname> and likely exit from the macro if the value
330    equaled <constant>null</constant>.
331  </para>
332
333  <para>
334    A set of options can be similarly placed in a
335    <classname>JComboBox</classname> component created as part of a larger
336    dialog or <function>showMessageDialog()</function> layout.  Here are some
337    code fragments showing this approach:
338  </para>
339
340  <informalexample>
341  <programlisting>// fragments from Display_Abbreviations.bsh
342// import statements and other code omitted
343
344// from main routine, this method call returns an array
345// of Strings representing the names of abbreviation sets
346
347abbrevSets = getActiveSets();
348
349...
350
351// from showAbbrevs() method
352
353combo = new JComboBox(abbrevSets);
354// set width to uniform size regardless of combobox contents
355Dimension dim = combo.getPreferredSize();
356dim.width = Math.max(dim.width, 120);
357combo.setPreferredSize(dim);
358combo.setSelectedItem(STARTING_SET); // defined as "global"
359
360// end fragments</programlisting>
361  </informalexample>
362
363</sect2>
364
365
366<sect2 id="macro-tips-single-char"><title>Using a Single Keypress as Input</title>
367
368  <para>
369    Some macros may choose to emulate the style of character-based text
370    editors such as <application>emacs</application> or
371    <application>vi</application>. They will require only a single keypress
372    as input that would be handled by the macro but not displayed on the
373    screen. If the keypress corresponds to a character value, jEdit can pass
374    that value as a parameter to a BeanShell script.
375  </para>
376
377  <para>
378    The jEdit class <ulink url="../api/org/gjt/sp/jedit/gui/InputHandler.html">InputHandler</ulink> is an abstract class
379    that that manages associations between keyboard input and editing
380    actions, along with the recording of macros. Keyboard input
381    in jEdit is normally managed by the derived class
382    <ulink url="../api/org/gjt/sp/jedit/gui/DefaultInputHandler.html">DefaultInputHandler</ulink>. One of the methods in
383    the <ulink url="../api/org/gjt/sp/jedit/gui/InputHandler.html">InputHandler</ulink> class handles input from a
384    single keypress:
385  </para>
386
387  <itemizedlist>
388    <listitem>
389      <funcsynopsis>
390        <funcprototype>
391          <funcdef>public void <function>readNextChar</function></funcdef>
392          <paramdef>String <parameter>prompt</parameter></paramdef>
393          <paramdef>String <parameter>code</parameter></paramdef>
394        </funcprototype>
395      </funcsynopsis>
396    </listitem>
397  </itemizedlist>
398
399  <para>
400    When this method is called, the contents of the <varname>prompt</varname>
401    parameter is shown in the view's status
402    bar. The method then waits for a key press, after which
403    the contents of the <varname>code</varname> parameter will be run as
404    a BeanShell script, with one important modification. Each time the
405    string <varname>__char__</varname> appears in the parameter script, it
406    will be substituted by the character pressed. The key press
407    is <quote>consumed</quote> by <function>readNextChar()</function>.
408    It will not be displayed on the screen or otherwise processed by jEdit.
409  </para>
410
411  <para>
412    Using <function>readNextChar()</function> requires a macro within the
413    macro, formatted as a single, potentially lengthy string literal. The
414    following macro illustrates this technique. It selects a line of text
415    from the current caret position to the first occurrence of the character
416    next typed by the user. If the character does not appear on the line, no
417    new selection occurs and the display remains unchanged.
418  </para>
419
420  <informalexample>
421  <programlisting>// Next_Char.bsh
422
423script = new StringBuffer(512);
424script.append( "start = textArea.getCaretPosition();"         );
425script.append( "line = textArea.getCaretLine();"              );
426script.append( "end = textArea.getLineEndOffset(line) + 1;"   );
427script.append( "text = buffer.getText(start, end - start);"   );
428script.append( "match = text.indexOf(__char__, 1);"           );
429script.append( "if(match != -1) {"                            );
430script.append(   "if(__char__ != '\\n') ++match;"             );
431script.append(   "textArea.select(start, start + match - 1);" );
432script.append( "}"                                            );
433
434view.getInputHandler().readNextChar("Enter a character",
435    script.toString());
436
437// end Next_Char.bsh</programlisting>
438  </informalexample>
439
440  <para>
441    Once again, here are a few comments on the macro's design.
442  </para>
443
444  <itemizedlist>
445    <listitem>
446      <para>
447        A <classname>StringBuffer</classname> object is used for efficiency; it
448        obviates multiple creation of fixed-length <classname>String</classname>
449        objects. The parameter to the constructor of <varname>script</varname>
450        specifies the initial size of the buffer that will receive the contents
451        of the child script.
452      </para>
453    </listitem>
454
455    <listitem>
456      <para>
457        Besides the quoting of the script code, the formatting of the macro is
458        entirely optional but (hopefully) makes it easier to read.
459      </para>
460    </listitem>
461
462    <listitem>
463      <para>
464        It is important that the child script be self-contained.  It does
465        not run in the same namespace as the <quote>parent</quote> macro
466        <filename>Next_Char.bsh</filename> and therefore does not share
467        variables, methods, or scripted objects defined in the parent
468        macro.
469      </para>
470    </listitem>
471
472    <listitem>
473      <para>
474        Finally, access to the <ulink url="../api/org/gjt/sp/jedit/gui/InputHandler.html">InputHandler</ulink> object used
475        by jEdit is available by calling
476        <function>getInputHandler()</function> on the current view.
477      </para>
478    </listitem>
479
480  </itemizedlist>
481
482</sect2>
483
484</sect1>
485
486
487<sect1 id="startup-scripts"><title>Startup Scripts</title>
488
489  <para>
490    On startup, jEdit runs any BeanShell scripts located in the
491    <filename>startup</filename> subdirectory of the jEdit installation and
492    user settings directories (see <xref linkend="settings-directory" />). As
493    with macros, the scripts must have a <filename>.bsh</filename> file name
494    extension. Startup scripts are run near the end of the startup sequence,
495    after plugins, properties and such have been initialized, but before the
496    first view is opened.
497  </para>
498
499  <para>
500    Startup scripts can perform initialization tasks that cannot be handled
501    by command line options or ordinary configuration options, such as
502    customizing jEdit's user interface by
503    changing entries in the Java platform's
504    <classname>UIManager</classname> class.
505  </para>
506
507  <para>
508    Startup scripts have an additional feature lacking in ordinary macros
509    that can help you further customize jEdit. Variables and methods
510    defined in a startup script are available in all instances of the BeanShell
511    interpreter created in jEdit. This allows you to create a personal
512    library of methods and objects that can be accessed at any time during
513    the editing session in another macro, the BeanShell shell of the Console
514    plugin, or menu items such as
515    <guimenu>Utilities</guimenu>&gt;<guisubmenu>BeanShell</guisubmenu>&gt;<guimenuitem>Evaluate
516    BeanShell Expression</guimenuitem>.
517  </para>
518
519  <para>
520    The startup script routine will run script files in the installation
521    directory first, followed by scripts in the user settings directory. In
522    each case, scripts will be executed in alphabetical order, applied
523    without regard to whether the file name contains upper or lower case
524    characters.
525  </para>
526
527  <para>
528    If a startup script throws an exception (because, for example,
529    it attempts to call a method on a <constant>null</constant> object).
530    jEdit will show an error dialog box and move on to the next
531    startup script. If script bugs are causing jEdit to crash or
532    hang on startup, you can use the
533    <userinput>-nostartupscripts</userinput> command line option to
534    disable them for that editing session.
535  </para>
536
537  <para>
538    Another important difference between startup scripts and ordinary
539    macros is that startup scripts cannot use the pre-defined variables
540    <varname>view</varname>, <varname>textArea</varname>,
541    <varname>editPane</varname> and <varname>buffer</varname>. This is
542    because they are executed before the initial view is created.
543  </para>
544
545  <para>
546    If you are writing a method in a
547    startup script and wish to use one of the above variables, pass
548    parameters of the
549    appropriate type to the method, so that a macro calling them after
550    startup can supply the appropriate values. For example, a startup script
551    could include a method
552  </para>
553
554  <informalexample><programlisting>void doSomethingWithView(View v, String s)  {
555    ...
556}</programlisting></informalexample>
557
558  <para>
559    so that during the editing session another macro can call the method
560    using
561  </para>
562
563  <informalexample><programlisting>doSomethingWithView(view, "something");</programlisting></informalexample>
564
565  <sidebar><title>Reloading startup scripts without restarting</title>
566   <para>
567    It is actually possible to reload startup scripts or load other scripts
568    without restarting
569    jEdit, using a BeanShell statement like the following:
570   </para>
571   <programlisting>BeanShell.runScript(view,<replaceable>path</replaceable>,null,false);</programlisting>
572   <para>
573    For <replaceable>path</replaceable>, you can substitute any string,
574    or a method call such as <function>buffer.getPath()</function>.
575   </para>
576  </sidebar>
577</sect1>
578
579<sect1 id="scripts-command-line"><title>Running Scripts from the Command
580Line</title>
581
582<para>
583  The <userinput>-run</userinput> command line switch specifies a BeanShell
584  script to run on startup:
585</para>
586
587<screen><prompt>$ </prompt><userinput>jedit -run=test.bsh</userinput></screen>
588
589<para>
590  Note that just like with startup scripts, the <varname>view</varname>,
591  <varname>textArea</varname>, <varname>editPane</varname> and
592  <varname>buffer</varname> variables are not defined.
593</para>
594
595<para>
596  If another instance is already running, the script will be run in that
597  instance, and you will be able to use the
598  <function>jEdit.getLastView()</function> method to obtain a view.
599  However, if a new instance of jEdit is being started, the script will
600  be run at the same time as all other startup scripts; that is, before
601  the first view is opened.
602</para>
603
604<para>
605  If your script needs a view instance to operate on, you can use the
606  following code pattern to obtain one, no matter how or when the script is
607  being run:
608</para>
609
610<programlisting>void doSomethingUseful()
611{
612    void run()
613    {
614        view = jEdit.getLastView();
615
616        // put actual script body here
617    }
618
619    if(jEdit.getLastView() == null)
620        VFSManager.runInAWTThread(this);
621    else
622        run();
623}
624
625doSomethingUseful();</programlisting>
626
627<para>
628  If the script is being run in a loaded
629  instance, it can be invoked to perform its work immediately.
630  However, if
631  the script is running at startup, before an initial view exists, its
632  operation must be delayed to allow the view object first to be created
633  and displayed.
634  In order to queue the macro's operation, the scripted <quote>closure</quote>
635  named <function>doSomethingUseful()</function> implements the
636  <classname>Runnable</classname> interface of the Java platform.
637  That interface contains only
638  a single <function>run()</function> method that takes no parameters
639  and has no return value. The macro's implementation of the
640  <function>run()</function> method contains the <quote>working</quote>
641  portion of the macro.  Then the scripted object, represented by a
642  reference to <varname>this</varname>, is passed to the
643  <function>runInAWTThread()</function> method. This schedules the
644  macro's operations for execution after the startup routine is complete.
645</para>
646
647<para>
648  As this example illustrates, the <function>runInAWTThread()</function>
649  method can be used to ensure that a macro will perform operations
650  after other operations have
651  completed. If it is invoked during startup, it schedules the specified
652  <classname>Runnable</classname> object to run after startup is complete.
653  If invoked when jEdit is fully loaded, the <classname>Runnable</classname>
654  object will execute after
655  all pending input/output is complete, or immediately if there are no
656  pending I/O operations. This will delay operations on a new buffer,
657  for example, until after the buffer is loaded and displayed.
658</para>
659
660</sect1>
661
662<sect1 id="macro-tips-BeanShell"><title>Advanced BeanShell Techniques</title>
663
664<para>
665  BeanShell has a few advanced features that we haven't mentioned yet.
666  They will be discussed in this section.
667</para>
668
669<sect2 id="macro-tips-BeanShell-convenience">
670<title>BeanShell's Convenience Syntax</title>
671
672<para>
673  We noted earlier that BeanShell syntax does not require that variables
674  be declared or defined with their type, and that variables that are
675  not typed when first used can have values of differing types assigned
676  to them. In addition to this <quote>loose</quote> syntax, BeanShell
677  allows a <quote>convenience</quote> syntax for dealing with the
678  properties of JavaBeans.  They may be accessed or set as if they were
679  data members. They may also be accessed using the name of the property
680  enclosed in quotation marks and curly brackets.  For example, the following
681  statement are all equivalent, assuming <varname>btn</varname> is
682  a <classname>JButton</classname> instance:
683</para>
684
685<informalexample><programlisting>b.setText("Choose");
686b.text = "Choose";
687b{"text"} = "Choose";
688</programlisting></informalexample>
689
690<para>
691  The last form can also be used to access a key-value pair of a
692  <classname>Hashtable</classname> object.
693</para>
694
695<!-- actually, the following requires the bsh.classpath package, which
696     is not included with jEdit at this point in time.
697
698     a future release of jEdit will use bsh.classpath, and hence support
699     'import *'
700
701<para>
702  Finally, when importing classes, BeanShell permits the following form
703  to import all classes lying within the interpreter's classpath:
704</para>
705
706<informalexample><programlisting>import *;
707</programlisting></informalexample>
708
709-->
710
711</sect2>
712
713
714<sect2 id="macro-tips-BeanShell-keywords">
715<title>Special BeanShell Keywords</title>
716
717<para>
718  BeanShell uses special keywords to refer to variables or methods defined in
719  the current or an enclosing block's scope:
720</para>
721
722<itemizedlist>
723  <listitem>
724    <para>
725      The keyword <function>this</function> refers to the current scope.
726    </para>
727  </listitem>
728  <listitem>
729    <para>
730      The keyword <function>super</function> refers to the immediately
731      enclosing scope.
732    </para>
733  </listitem>
734  <listitem>
735    <para>
736      The keyword <function>global</function> refers to the top-level
737      scope of the macro script.
738    </para>
739  </listitem>
740</itemizedlist>
741
742<para>
743  The following script illustrates the use of these keywords:
744</para>
745
746<informalexample><programlisting>a = "top\n";
747foo() {
748    a = "middle\n";
749    bar() {
750        a = "bottom\n";
751        textArea.setSelectedText(global.a);
752        textArea.setSelectedText(super.a);
753        // equivalent to textArea.setSelectedText(this.a):
754        textArea.setSelectedText(a);
755    }
756
757    bar();
758}
759foo();</programlisting></informalexample>
760
761<para>
762  When the script is run, the following text is inserted in the current
763  buffer:
764</para>
765
766<screen>top
767middle
768bottom</screen>
769
770</sect2>
771
772<sect2 id="macro-tips-BeanShell-class">
773<title>Implementing Classes and Interfaces</title>
774
775<para>
776  As discussed in the macro example in <xref
777  linkend="dialog-macro"/>, scripted objects can implicitly implement Java
778  interfaces such as <classname>ActionListener</classname>. For example:
779</para>
780
781<programlisting><![CDATA[myRunnable() {
782    run() {
783        System.out.println("Hello world!");
784    }
785    
786    return this;
787}
788
789Runnable r = myRunnable();
790new Thread(r).start();]]></programlisting>
791
792<para>
793  Frequently it will not be necessary to implement all of the methods of
794  a particular interface in order to specify the behavior of a scripted
795  object. To prevent BeanShell from throwing exceptions for missing interface methods, implement the <function>invoke()</function> method,
796  which is called when an undefined method is invoked on a scripted
797  object. Typically, the implementation of this method will do nothing,
798  as in the following example:
799</para>
800
801<informalexample><programlisting>invoke(method, args) {}</programlisting>
802</informalexample>
803
804<para>
805  In addition to the implicit interface definitions described above,
806  BeanShell permits full-blown classes to be defined. Indeed, almost any Java class definition should work in BeanShell:
807</para>
808
809<programlisting><![CDATA[class Cons {
810    // Long-live LISP!
811    Object car;
812    Object cdr;
813
814    rplaca(Object car) {
815        this.car = car;
816    }
817
818    rplacd(Object cdr) {
819        this.cdr = cdr;
820    }
821}]]></programlisting>
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 extensible) 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 <ulink url="../api/org/gjt/sp/util/Log.html">Log</ulink>
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   See the documentation for the <ulink url="../api/org/gjt/sp/util/Log.html">Log</ulink> class for information about the method's parameters.
921  </para>
922
923  <para>
924    The following code sends a typical debugging message to the
925    activity log:
926  </para>
927
928    <informalexample><programlisting>Log.log(Log.DEBUG, BeanShell.class,
929    "counter = " + counter);</programlisting></informalexample>
930
931  <para>
932    The corresponding activity log entry might read as follows:
933  </para>
934
935    <informalexample>
936    <programlisting>[debug] BeanShell: counter = 15</programlisting>
937    </informalexample>
938
939  <sidebar><title>Using message dialog boxes as a tracing tool</title>
940    <para>
941      If you would prefer not having to deal with the activity log, you
942      can use the <function>Macros.message()</function> method as a
943      tracing tool. Just insert calls like the following in the macro
944      code:
945    </para>
946
947    <programlisting>Macros.message(view,"tracing");</programlisting>
948
949    <para>
950      Execution of the macro is halted until the message dialog box is
951      closed. When you have finished debugging the macro, you should
952      delete or comment out the debugging calls to
953      <function>Macros.message()</function> in your final source code.
954    </para>
955  </sidebar>
956
957</sect2>
958
959</sect1>
960
961</chapter>