PageRenderTime 68ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

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

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