PageRenderTime 51ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

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

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