PageRenderTime 39ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre3/org/gjt/sp/jedit/search/SearchAndReplace.java

#
Java | 955 lines | 574 code | 124 blank | 257 comment | 120 complexity | 8ddc422193a31ec9842a51d3f27a30e8 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. /*
  2. * SearchAndReplace.java - Search and replace
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 1999, 2000, 2001 Slava Pestov
  7. * Portions copyright (C) 2001 Tom Locke
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22. */
  23. package org.gjt.sp.jedit.search;
  24. //{{{ Imports
  25. import javax.swing.text.Segment;
  26. import javax.swing.JOptionPane;
  27. import java.awt.Component;
  28. import org.gjt.sp.jedit.io.VFSManager;
  29. import org.gjt.sp.jedit.msg.SearchSettingsChanged;
  30. import org.gjt.sp.jedit.textarea.*;
  31. import org.gjt.sp.jedit.*;
  32. import org.gjt.sp.util.Log;
  33. //}}}
  34. /**
  35. * Class that implements regular expression and literal search within
  36. * jEdit buffers.
  37. * @author Slava Pestov
  38. * @version $Id: SearchAndReplace.java 3933 2001-12-03 10:52:27Z spestov $
  39. */
  40. public class SearchAndReplace
  41. {
  42. //{{{ Getters and setters
  43. //{{{ setSearchString() method
  44. /**
  45. * Sets the current search string.
  46. * @param search The new search string
  47. */
  48. public static void setSearchString(String search)
  49. {
  50. if(search.equals(SearchAndReplace.search))
  51. return;
  52. SearchAndReplace.search = search;
  53. matcher = null;
  54. EditBus.send(new SearchSettingsChanged(null));
  55. } //}}}
  56. //{{{ getSearchString() method
  57. /**
  58. * Returns the current search string.
  59. */
  60. public static String getSearchString()
  61. {
  62. return search;
  63. } //}}}
  64. //{{{ setReplaceString() method
  65. /**
  66. * Sets the current replacement string.
  67. * @param search The new replacement string
  68. */
  69. public static void setReplaceString(String replace)
  70. {
  71. if(replace.equals(SearchAndReplace.replace))
  72. return;
  73. SearchAndReplace.replace = replace;
  74. matcher = null;
  75. EditBus.send(new SearchSettingsChanged(null));
  76. } //}}}
  77. //{{{ getRepalceString() method
  78. /**
  79. * Returns the current replacement string.
  80. */
  81. public static String getReplaceString()
  82. {
  83. return replace;
  84. } //}}}
  85. //{{{ setIgnoreCase() method
  86. /**
  87. * Sets the ignore case flag.
  88. * @param ignoreCase True if searches should be case insensitive,
  89. * false otherwise
  90. */
  91. public static void setIgnoreCase(boolean ignoreCase)
  92. {
  93. if(ignoreCase == SearchAndReplace.ignoreCase)
  94. return;
  95. SearchAndReplace.ignoreCase = ignoreCase;
  96. matcher = null;
  97. EditBus.send(new SearchSettingsChanged(null));
  98. } //}}}
  99. //{{{ getIgnoreCase() method
  100. /**
  101. * Returns the state of the ignore case flag.
  102. * @return True if searches should be case insensitive,
  103. * false otherwise
  104. */
  105. public static boolean getIgnoreCase()
  106. {
  107. return ignoreCase;
  108. } //}}}
  109. //{{{ setRegexp() method
  110. /**
  111. * Sets the state of the regular expression flag.
  112. * @param regexp True if regular expression searches should be
  113. * performed
  114. */
  115. public static void setRegexp(boolean regexp)
  116. {
  117. if(regexp == SearchAndReplace.regexp)
  118. return;
  119. SearchAndReplace.regexp = regexp;
  120. matcher = null;
  121. EditBus.send(new SearchSettingsChanged(null));
  122. } //}}}
  123. //{{{ getRegexp() method
  124. /**
  125. * Returns the state of the regular expression flag.
  126. * @return True if regular expression searches should be performed
  127. */
  128. public static boolean getRegexp()
  129. {
  130. return regexp;
  131. } //}}}
  132. //{{{ setReverseSearch() method
  133. /**
  134. * Sets the reverse search flag. Note that currently, only literal
  135. * reverse searches are supported.
  136. * @param reverse True if searches should go backwards,
  137. * false otherwise
  138. */
  139. public static void setReverseSearch(boolean reverse)
  140. {
  141. if(reverse == SearchAndReplace.reverse)
  142. return;
  143. SearchAndReplace.reverse = reverse;
  144. matcher = null;
  145. EditBus.send(new SearchSettingsChanged(null));
  146. } //}}}
  147. //{{{ getReverseSearch() method
  148. /**
  149. * Returns the state of the reverse search flag.
  150. * @return True if searches should go backwards,
  151. * false otherwise
  152. */
  153. public static boolean getReverseSearch()
  154. {
  155. return reverse;
  156. } //}}}
  157. //{{{ setBeanShellReplace() method
  158. /**
  159. * Sets the state of the BeanShell replace flag.
  160. * @param regexp True if the replace string is a BeanShell expression
  161. * @since jEdit 3.2pre2
  162. */
  163. public static void setBeanShellReplace(boolean beanshell)
  164. {
  165. if(beanshell == SearchAndReplace.beanshell)
  166. return;
  167. SearchAndReplace.beanshell = beanshell;
  168. matcher = null;
  169. EditBus.send(new SearchSettingsChanged(null));
  170. } //}}}
  171. //{{{ getBeanShellReplace() method
  172. /**
  173. * Returns the state of the BeanShell replace flag.
  174. * @return True if the replace string is a BeanShell expression
  175. * @since jEdit 3.2pre2
  176. */
  177. public static boolean getBeanShellReplace()
  178. {
  179. return beanshell;
  180. } //}}}
  181. //{{{ setAutoWrap() method
  182. /**
  183. * Sets the state of the auto wrap around flag.
  184. * @param wrap If true, the 'continue search from start' dialog
  185. * will not be displayed
  186. * @since jEdit 3.2pre2
  187. */
  188. public static void setAutoWrapAround(boolean wrap)
  189. {
  190. if(wrap == SearchAndReplace.wrap)
  191. return;
  192. SearchAndReplace.wrap = wrap;
  193. EditBus.send(new SearchSettingsChanged(null));
  194. } //}}}
  195. //{{{ getAutoWrap() method
  196. /**
  197. * Returns the state of the auto wrap around flag.
  198. * @param wrap If true, the 'continue search from start' dialog
  199. * will not be displayed
  200. * @since jEdit 3.2pre2
  201. */
  202. public static boolean getAutoWrapAround()
  203. {
  204. return wrap;
  205. } //}}}
  206. //{{{ setSearchMatcher() method
  207. /**
  208. * Sets the current search string matcher. Note that calling
  209. * <code>setSearchString</code>, <code>setReplaceString</code>,
  210. * <code>setIgnoreCase</code> or <code>setRegExp</code> will
  211. * reset the matcher to the default.
  212. */
  213. public static void setSearchMatcher(SearchMatcher matcher)
  214. {
  215. SearchAndReplace.matcher = matcher;
  216. EditBus.send(new SearchSettingsChanged(null));
  217. } //}}}
  218. //{{{ getSearchMatcher() method
  219. /**
  220. * Returns the current search string matcher.
  221. * @exception IllegalArgumentException if regular expression search
  222. * is enabled, the search string or replacement string is invalid
  223. */
  224. public static SearchMatcher getSearchMatcher()
  225. throws Exception
  226. {
  227. return getSearchMatcher(true);
  228. } //}}}
  229. //{{{ getSearchMatcher() method
  230. /**
  231. * Returns the current search string matcher.
  232. * @param reverseOK Replacement commands need a non-reversed matcher,
  233. * so they set this to false
  234. * @exception IllegalArgumentException if regular expression search
  235. * is enabled, the search string or replacement string is invalid
  236. */
  237. public static SearchMatcher getSearchMatcher(boolean reverseOK)
  238. throws Exception
  239. {
  240. reverseOK &= (fileset instanceof CurrentBufferSet);
  241. if(matcher != null && (reverseOK || !reverse))
  242. return matcher;
  243. if(search == null || "".equals(search))
  244. return null;
  245. // replace must not be null
  246. String replace = (SearchAndReplace.replace == null ? "" : SearchAndReplace.replace);
  247. String replaceMethod;
  248. if(beanshell && replace.length() != 0)
  249. {
  250. replaceMethod = BeanShell.cacheBlock("replace","return ("
  251. + replace + ");",false);
  252. }
  253. else
  254. replaceMethod = null;
  255. if(regexp)
  256. matcher = new RESearchMatcher(search,replace,ignoreCase,
  257. beanshell,replaceMethod);
  258. else
  259. {
  260. matcher = new BoyerMooreSearchMatcher(search,replace,
  261. ignoreCase,reverse && reverseOK,beanshell,
  262. replaceMethod);
  263. }
  264. return matcher;
  265. } //}}}
  266. //{{{ setSearchFileSet() method
  267. /**
  268. * Sets the current search file set.
  269. * @param fileset The file set to perform searches in
  270. */
  271. public static void setSearchFileSet(SearchFileSet fileset)
  272. {
  273. SearchAndReplace.fileset = fileset;
  274. EditBus.send(new SearchSettingsChanged(null));
  275. } //}}}
  276. //{{{ getSearchFileSet() method
  277. /**
  278. * Returns the current search file set.
  279. */
  280. public static SearchFileSet getSearchFileSet()
  281. {
  282. return fileset;
  283. } //}}}
  284. //}}}
  285. //{{{ Actions
  286. //{{{ hyperSearch() method
  287. /**
  288. * Performs a HyperSearch.
  289. * @param view The view
  290. * @since jEdit 2.7pre3
  291. */
  292. public static boolean hyperSearch(View view)
  293. {
  294. return hyperSearch(view,false);
  295. } //}}}
  296. //{{{ hyperSearch() method
  297. /**
  298. * Performs a HyperSearch.
  299. * @param view The view
  300. * @param selection If true, will only search in the current selection.
  301. * Note that the file set must be the current buffer file set for this
  302. * to work.
  303. * @since jEdit 4.0pre1
  304. */
  305. public static boolean hyperSearch(View view, boolean selection)
  306. {
  307. record(view,"hyperSearch(view," + selection + ")",false,
  308. !selection);
  309. view.getDockableWindowManager().addDockableWindow(
  310. HyperSearchResults.NAME);
  311. final HyperSearchResults results = (HyperSearchResults)
  312. view.getDockableWindowManager()
  313. .getDockable(HyperSearchResults.NAME);
  314. results.searchStarted();
  315. try
  316. {
  317. Selection[] s;
  318. if(selection)
  319. {
  320. s = view.getTextArea().getSelection();
  321. if(s == null)
  322. return false;
  323. }
  324. else
  325. s = null;
  326. VFSManager.runInWorkThread(new HyperSearchRequest(view,
  327. getSearchMatcher(false),results,s));
  328. return true;
  329. }
  330. catch(Exception e)
  331. {
  332. Log.log(Log.ERROR,SearchAndReplace.class,e);
  333. Object[] args = { e.getMessage() };
  334. if(args[0] == null)
  335. args[0] = e.toString();
  336. GUIUtilities.error(view,"searcherror",args);
  337. return false;
  338. }
  339. } //}}}
  340. //{{{ find() method
  341. /**
  342. * Finds the next occurance of the search string.
  343. * @param view The view
  344. * @return True if the operation was successful, false otherwise
  345. */
  346. public static boolean find(View view)
  347. {
  348. boolean repeat = false;
  349. String path = fileset.getNextFile(view,null);
  350. if(path == null)
  351. return false;
  352. try
  353. {
  354. SearchMatcher matcher = getSearchMatcher(true);
  355. if(matcher == null)
  356. {
  357. view.getToolkit().beep();
  358. return false;
  359. }
  360. record(view,"find(view)",false,true);
  361. view.showWaitCursor();
  362. loop: for(;;)
  363. {
  364. while(path != null)
  365. {
  366. Buffer buffer = jEdit.openTemporary(
  367. view,null,path,false);
  368. /* this is stupid and misleading.
  369. * but 'path' is not used anywhere except
  370. * the above line, and if this is done
  371. * after the 'continue', then we will
  372. * either hang, or be forced to duplicate
  373. * it inside the buffer == null, or add
  374. * a 'finally' clause. you decide which one's
  375. * worse. */
  376. path = fileset.getNextFile(view,path);
  377. if(buffer == null)
  378. continue loop;
  379. // Wait for the buffer to load
  380. if(!buffer.isLoaded())
  381. VFSManager.waitForRequests();
  382. int start;
  383. if(view.getBuffer() == buffer && !repeat)
  384. {
  385. JEditTextArea textArea = view.getTextArea();
  386. Selection s = textArea.getSelectionAtOffset(
  387. textArea.getCaretPosition());
  388. if(s == null)
  389. start = textArea.getCaretPosition();
  390. else if(reverse)
  391. start = s.getStart();
  392. else
  393. start = s.getEnd();
  394. }
  395. else if(reverse)
  396. start = buffer.getLength();
  397. else
  398. start = 0;
  399. if(find(view,buffer,start))
  400. return true;
  401. }
  402. if(repeat)
  403. {
  404. if(!BeanShell.isScriptRunning())
  405. {
  406. view.getStatus().setMessageAndClear(
  407. jEdit.getProperty("view.status.search-not-found"));
  408. view.getToolkit().beep();
  409. }
  410. return false;
  411. }
  412. boolean restart;
  413. if(BeanShell.isScriptRunning())
  414. {
  415. restart = true;
  416. }
  417. else if(wrap)
  418. {
  419. view.getStatus().setMessageAndClear(
  420. jEdit.getProperty("view.status.auto-wrap"));
  421. // beep if beep property set
  422. if(jEdit.getBooleanProperty("search.beepOnSearchAutoWrap"))
  423. {
  424. view.getToolkit().beep();
  425. }
  426. restart = true;
  427. }
  428. else
  429. {
  430. Integer[] args = { new Integer(reverse ? 1 : 0) };
  431. int result = GUIUtilities.confirm(view,
  432. "keepsearching",args,
  433. JOptionPane.YES_NO_OPTION,
  434. JOptionPane.QUESTION_MESSAGE);
  435. restart = (result == JOptionPane.YES_OPTION);
  436. }
  437. if(restart)
  438. {
  439. // start search from beginning
  440. path = fileset.getFirstFile(view);
  441. repeat = true;
  442. }
  443. else
  444. break loop;
  445. }
  446. }
  447. catch(Exception e)
  448. {
  449. Log.log(Log.ERROR,SearchAndReplace.class,e);
  450. Object[] args = { e.getMessage() };
  451. if(args[0] == null)
  452. args[0] = e.toString();
  453. GUIUtilities.error(view,"searcherror",args);
  454. }
  455. finally
  456. {
  457. view.hideWaitCursor();
  458. }
  459. return false;
  460. } //}}}
  461. //{{{ find() method
  462. /**
  463. * Finds the next instance of the search string in the specified
  464. * buffer.
  465. * @param view The view
  466. * @param buffer The buffer
  467. * @param start Location where to start the search
  468. */
  469. public static boolean find(final View view, final Buffer buffer, final int start)
  470. throws Exception
  471. {
  472. SearchMatcher matcher = getSearchMatcher(true);
  473. Segment text = new Segment();
  474. if(reverse)
  475. buffer.getText(0,start,text);
  476. else
  477. buffer.getText(start,buffer.getLength() - start,text);
  478. // the start and end flags will be wrong with reverse search enabled,
  479. // but they are only used by the regexp matcher, which doesn't
  480. // support reverse search yet.
  481. //
  482. // REMIND: fix flags when adding reverse regexp search.
  483. int[] match = matcher.nextMatch(new CharIndexedSegment(text,reverse),
  484. start == 0,true);
  485. if(match != null)
  486. {
  487. jEdit.commitTemporary(buffer);
  488. view.setBuffer(buffer);
  489. JEditTextArea textArea = view.getTextArea();
  490. if(reverse)
  491. {
  492. textArea.setSelection(new Selection.Range(
  493. start - match[1],
  494. start - match[0]));
  495. textArea.moveCaretPosition(start - match[1]);
  496. }
  497. else
  498. {
  499. textArea.setSelection(new Selection.Range(
  500. start + match[0],
  501. start + match[1]));
  502. textArea.moveCaretPosition(start + match[1]);
  503. }
  504. return true;
  505. }
  506. else
  507. return false;
  508. } //}}}
  509. //{{{ replace() method
  510. /**
  511. * Replaces the current selection with the replacement string.
  512. * @param view The view
  513. * @return True if the operation was successful, false otherwise
  514. */
  515. public static boolean replace(View view)
  516. {
  517. JEditTextArea textArea = view.getTextArea();
  518. Selection[] selection = textArea.getSelection();
  519. if(selection.length == 0)
  520. {
  521. view.getToolkit().beep();
  522. return false;
  523. }
  524. record(view,"replace(view)",true,false);
  525. Buffer buffer = view.getBuffer();
  526. try
  527. {
  528. buffer.beginCompoundEdit();
  529. int retVal = 0;
  530. for(int i = 0; i < selection.length; i++)
  531. {
  532. Selection s = selection[i];
  533. /* if an occurence occurs at the
  534. beginning of the selection, the
  535. selection start will get moved.
  536. this sucks, so we hack to avoid it. */
  537. int start = s.getStart();
  538. retVal += _replace(view,buffer,
  539. s.getStart(),s.getEnd());
  540. // this has no effect if the selection
  541. // no longer exists
  542. textArea.removeFromSelection(s);
  543. if(s instanceof Selection.Range)
  544. {
  545. textArea.addToSelection(new Selection.Range(
  546. start,s.getEnd()));
  547. }
  548. else if(s instanceof Selection.Rect)
  549. {
  550. textArea.addToSelection(new Selection.Rect(
  551. start,s.getEnd()));
  552. }
  553. }
  554. if(retVal == 0)
  555. {
  556. view.getToolkit().beep();
  557. return false;
  558. }
  559. return true;
  560. }
  561. catch(Exception e)
  562. {
  563. Log.log(Log.ERROR,SearchAndReplace.class,e);
  564. Object[] args = { e.getMessage() };
  565. if(args[0] == null)
  566. args[0] = e.toString();
  567. GUIUtilities.error(view,"searcherror",args);
  568. }
  569. finally
  570. {
  571. buffer.endCompoundEdit();
  572. }
  573. return false;
  574. } //}}}
  575. //{{{ replace() method
  576. /**
  577. * Replaces text in the specified range with the replacement string.
  578. * @param view The view
  579. * @param buffer The buffer
  580. * @param start The start offset
  581. * @param end The end offset
  582. * @return True if the operation was successful, false otherwise
  583. */
  584. public static boolean replace(View view, Buffer buffer, int start, int end)
  585. {
  586. JEditTextArea textArea = view.getTextArea();
  587. try
  588. {
  589. int retVal = 0;
  590. buffer.beginCompoundEdit();
  591. retVal += _replace(view,buffer,start,end);
  592. if(retVal != 0)
  593. return true;
  594. }
  595. catch(Exception e)
  596. {
  597. Log.log(Log.ERROR,SearchAndReplace.class,e);
  598. Object[] args = { e.getMessage() };
  599. if(args[0] == null)
  600. args[0] = e.toString();
  601. GUIUtilities.error(view,"searcherror",args);
  602. }
  603. finally
  604. {
  605. buffer.endCompoundEdit();
  606. }
  607. return false;
  608. } //}}}
  609. //{{{ replaceAll() method
  610. /**
  611. * Replaces all occurances of the search string with the replacement
  612. * string.
  613. * @param view The view
  614. */
  615. public static boolean replaceAll(View view)
  616. {
  617. int fileCount = 0;
  618. int occurCount = 0;
  619. record(view,"replaceAll(view)",true,true);
  620. view.showWaitCursor();
  621. try
  622. {
  623. String path = fileset.getFirstFile(view);
  624. loop: while(path != null)
  625. {
  626. Buffer buffer = jEdit.openTemporary(
  627. view,null,path,false);
  628. /* this is stupid and misleading.
  629. * but 'path' is not used anywhere except
  630. * the above line, and if this is done
  631. * after the 'continue', then we will
  632. * either hang, or be forced to duplicate
  633. * it inside the buffer == null, or add
  634. * a 'finally' clause. you decide which one's
  635. * worse. */
  636. path = fileset.getNextFile(view,path);
  637. if(buffer == null)
  638. continue loop;
  639. // Wait for buffer to finish loading
  640. if(buffer.isPerformingIO())
  641. VFSManager.waitForRequests();
  642. // Leave buffer in a consistent state if
  643. // an error occurs
  644. try
  645. {
  646. buffer.beginCompoundEdit();
  647. int retVal = _replace(view,buffer,
  648. 0,buffer.getLength());
  649. if(retVal != 0)
  650. {
  651. fileCount++;
  652. occurCount += retVal;
  653. jEdit.commitTemporary(buffer);
  654. }
  655. }
  656. finally
  657. {
  658. buffer.endCompoundEdit();
  659. }
  660. }
  661. }
  662. catch(Exception e)
  663. {
  664. Log.log(Log.ERROR,SearchAndReplace.class,e);
  665. Object[] args = { e.getMessage() };
  666. if(args[0] == null)
  667. args[0] = e.toString();
  668. GUIUtilities.error(view,"searcherror",args);
  669. }
  670. finally
  671. {
  672. view.hideWaitCursor();
  673. }
  674. /* Don't do this when playing a macro, cos it's annoying */
  675. if(!BeanShell.isScriptRunning())
  676. {
  677. Object[] args = { new Integer(occurCount),
  678. new Integer(fileCount) };
  679. view.getStatus().setMessageAndClear(jEdit.getProperty(
  680. "view.status.replace-all",args));
  681. }
  682. return (fileCount != 0);
  683. } //}}}
  684. //}}}
  685. //{{{ load() method
  686. /**
  687. * Loads search and replace state from the properties.
  688. */
  689. public static void load()
  690. {
  691. search = jEdit.getProperty("search.find.value");
  692. replace = jEdit.getProperty("search.replace.value");
  693. ignoreCase = jEdit.getBooleanProperty("search.ignoreCase.toggle");
  694. regexp = jEdit.getBooleanProperty("search.regexp.toggle");
  695. beanshell = jEdit.getBooleanProperty("search.beanshell.toggle");
  696. wrap = jEdit.getBooleanProperty("search.wrap.toggle");
  697. fileset = new CurrentBufferSet();
  698. // Tags plugin likes to call this method at times other than
  699. // startup; so we need to fire a SearchSettingsChanged to
  700. // notify the search bar and so on.
  701. matcher = null;
  702. EditBus.send(new SearchSettingsChanged(null));
  703. } //}}}
  704. //{{{ save() method
  705. /**
  706. * Saves search and replace state to the properties.
  707. */
  708. public static void save()
  709. {
  710. jEdit.setProperty("search.find.value",search);
  711. jEdit.setProperty("search.replace.value",replace);
  712. jEdit.setBooleanProperty("search.ignoreCase.toggle",ignoreCase);
  713. jEdit.setBooleanProperty("search.regexp.toggle",regexp);
  714. jEdit.setBooleanProperty("search.beanshell.toggle",beanshell);
  715. jEdit.setBooleanProperty("search.wrap.toggle",wrap);
  716. } //}}}
  717. //{{{ Private members
  718. //{{{ Instance variables
  719. private static String search;
  720. private static String replace;
  721. private static boolean regexp;
  722. private static boolean ignoreCase;
  723. private static boolean reverse;
  724. private static boolean beanshell;
  725. private static boolean wrap;
  726. private static SearchMatcher matcher;
  727. private static SearchFileSet fileset;
  728. //}}}
  729. //{{{ record() method
  730. private static void record(View view, String action,
  731. boolean replaceAction, boolean recordFileSet)
  732. {
  733. Macros.Recorder recorder = view.getMacroRecorder();
  734. if(recorder != null)
  735. {
  736. recorder.record("SearchAndReplace.setSearchString(\""
  737. + MiscUtilities.charsToEscapes(search) + "\");");
  738. if(replaceAction)
  739. {
  740. recorder.record("SearchAndReplace.setReplaceString(\""
  741. + MiscUtilities.charsToEscapes(replace) + "\");");
  742. recorder.record("SearchAndReplace.setBeanShellReplace("
  743. + beanshell + ");");
  744. }
  745. else
  746. {
  747. // only record this if doing a find next
  748. recorder.record("SearchAndReplace.setAutoWrapAround("
  749. + wrap + ");");
  750. recorder.record("SearchAndReplace.setReverseSearch("
  751. + reverse + ");");
  752. }
  753. recorder.record("SearchAndReplace.setIgnoreCase("
  754. + ignoreCase + ");");
  755. recorder.record("SearchAndReplace.setRegexp("
  756. + regexp + ");");
  757. if(recordFileSet)
  758. {
  759. recorder.record("SearchAndReplace.setSearchFileSet("
  760. + fileset.getCode() + ");");
  761. }
  762. recorder.record("SearchAndReplace." + action + ";");
  763. }
  764. } //}}}
  765. //{{{ _replace() method
  766. /**
  767. * Replaces all occurances of the search string with the replacement
  768. * string.
  769. * @param view The view
  770. * @param buffer The buffer
  771. * @param start The start offset
  772. * @param end The end offset
  773. * @return True if the replace operation was successful, false
  774. * if no matches were found
  775. */
  776. private static int _replace(View view, Buffer buffer,
  777. int start, int end) throws Exception
  778. {
  779. if(!buffer.isEditable())
  780. return 0;
  781. SearchMatcher matcher = getSearchMatcher(false);
  782. if(matcher == null)
  783. return 0;
  784. int occurCount = 0;
  785. boolean smartCaseReplace = (TextUtilities.getStringCase(replace)
  786. == TextUtilities.LOWER_CASE);
  787. Segment text = new Segment();
  788. int offset = start;
  789. loop: for(;;)
  790. {
  791. buffer.getText(offset,end - offset,text);
  792. int[] occur = matcher.nextMatch(
  793. new CharIndexedSegment(text,false),
  794. start == 0,end == buffer.getLength());
  795. if(occur == null)
  796. break loop;
  797. int _start = occur[0];
  798. int _length = occur[1] - occur[0];
  799. String found = new String(text.array,text.offset + _start,_length);
  800. String subst = matcher.substitute(found);
  801. if(smartCaseReplace && ignoreCase)
  802. {
  803. int strCase = TextUtilities.getStringCase(found);
  804. if(strCase == TextUtilities.LOWER_CASE)
  805. subst = subst.toLowerCase();
  806. else if(strCase == TextUtilities.UPPER_CASE)
  807. subst = subst.toUpperCase();
  808. else if(strCase == TextUtilities.TITLE_CASE)
  809. subst = TextUtilities.toTitleCase(subst);
  810. }
  811. if(subst != null)
  812. {
  813. buffer.remove(offset + _start,_length);
  814. buffer.insert(offset + _start,subst);
  815. occurCount++;
  816. offset += _start + subst.length();
  817. if(subst.length() == 0 && _length == 0)
  818. offset++;
  819. end += (subst.length() - found.length());
  820. }
  821. else if(_length == 0)
  822. {
  823. // avoid infinite loop
  824. offset += _start + 1;
  825. }
  826. else
  827. offset += _start + _length;
  828. }
  829. return occurCount;
  830. } //}}}
  831. }