/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><</quote>).append(tag).append(<quote>></quote>);
48 sb.append(text);
49 sb.append(<quote></</quote>).append(tag).append(<quote>></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>><guisubmenu>BeanShell</guisubmenu>><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>