PageRenderTime 53ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

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

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