Source code: com/port80/eclipse/jdt/actions/RegexReplaceDialog.java
1 package com.port80.eclipse.jdt.actions;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import org.apache.oro.text.perl.Perl5Util;
7 import org.apache.oro.text.regex.PatternMatcherInput;
8 import org.eclipse.jface.dialogs.Dialog;
9 import org.eclipse.jface.text.BadLocationException;
10 import org.eclipse.jface.text.IDocument;
11 import org.eclipse.jface.text.Position;
12 import org.eclipse.jface.text.TextSelection;
13 import org.eclipse.jface.viewers.ISelection;
14 import org.eclipse.swt.SWT;
15 import org.eclipse.swt.events.DisposeEvent;
16 import org.eclipse.swt.events.DisposeListener;
17 import org.eclipse.swt.events.ModifyEvent;
18 import org.eclipse.swt.events.ModifyListener;
19 import org.eclipse.swt.events.SelectionAdapter;
20 import org.eclipse.swt.events.SelectionEvent;
21 import org.eclipse.swt.events.ShellAdapter;
22 import org.eclipse.swt.events.ShellEvent;
23 import org.eclipse.swt.graphics.Color;
24 import org.eclipse.swt.graphics.Font;
25 import org.eclipse.swt.graphics.FontMetrics;
26 import org.eclipse.swt.graphics.GC;
27 import org.eclipse.swt.graphics.Point;
28 import org.eclipse.swt.layout.GridData;
29 import org.eclipse.swt.widgets.Button;
30 import org.eclipse.swt.widgets.Combo;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Control;
33 import org.eclipse.swt.widgets.Label;
34 import org.eclipse.swt.widgets.Shell;
35 import org.eclipse.ui.texteditor.ITextEditor;
36
37 import com.port80.eclipse.jdt.JdtPlugin;
38 import com.port80.eclipse.jdt.ThemeManager;
39 import com.port80.eclipse.util.UIUtil;
40 import com.port80.util.struct.IntList;
41
42 /**
43 * @author chrisl
44 *
45 * To change this generated comment edit the template variable "typecomment":
46 * Window>Preferences>Java>Templates.
47 * To enable and disable the creation of type comments go to
48 * Window>Preferences>Java>Code Generation.
49 */
50 public class RegexReplaceDialog extends Dialog {
51
52 ////////////////////////////////////////////////////////////////////////
53
54 private static final String NAME = "RegexReplaceDialog";
55 private static final boolean DEBUG = false;
56 private static final int MAX_PERSISTENT = 8;
57 private static final int WIDTH=32;
58
59 ////////////////////////////////////////////////////////////////////////
60
61 private static RegexReplaceDialog fDefault;
62 private static Point fWindowPosition;
63 private static List fPatterns = new ArrayList();
64 private static List fReplaces = new ArrayList();
65 private static Perl5Util fReEngine;
66
67 private Shell fParent;
68 private ITextEditor fEditor;
69 private IDocument fDoc;
70 PatternMatcherInput fMatchInput;
71 // Start of search region.
72 int fStart;
73 // End of search region. A 0-length Position is required so that it would be adjusted
74 // when on replacements.
75 Position fEnd;
76 // Current search starting point.
77 int fOffset;
78 // Incremental search anchor, allow incremental search to restart at anchor location
79 // on pattern changes.
80 int fAnchor;
81 int fLastReplaced;
82 IntList fHistory = new IntList();
83 PatternChanged fPatternChangedHandler = new PatternChanged();
84 boolean isWrapped;
85 boolean fReplaceAllRunning;
86 //
87 private Combo fPattern;
88 private Combo fReplace;
89 private Button fButtonReplaceAll;
90 private Button fButtonReplaceClose;
91 private Button fButtonRelaceFind;
92 private Button fButtonFind;
93 private Button fButtonPrev;
94 private Button fButtonNext;
95 private Button fSingleLine;
96 private Button fCaseSensitive;
97 private Button fButtonClose;
98 private Label fStatus;
99
100 ////////////////////////////////////////////////////////////////////////
101
102 public static RegexReplaceDialog getDefault(Shell shell, ITextEditor editor) {
103 if (fDefault == null || fDefault.getShell() == null || fDefault.getShell().isDisposed()) {
104 fDefault = new RegexReplaceDialog(shell, editor);
105 } else {
106 if (DEBUG)
107 System.err.println("getDefault()");
108 fDefault.fEditor = editor;
109 fWindowPosition = fDefault.getShell().getLocation();
110 }
111 return fDefault;
112 }
113
114 /**
115 * Constructor for RegexReplaceDialog.
116 * @param parentShell
117 */
118 private RegexReplaceDialog(Shell parentShell, ITextEditor editor) {
119 super(parentShell);
120 fParent = parentShell;
121 fEditor = editor;
122 //
123 setShellStyle(SWT.DIALOG_TRIM);
124 setBlockOnOpen(false);
125 }
126
127 ////////////////////////////////////////////////////////////////////////
128
129 public void dispose() {
130 if (DEBUG)
131 System.err.println("dispose()");
132 if (fEnd != null) {
133 fDoc.removePosition(fEnd);
134 fEnd = null;
135 }
136 fReEngine = null;
137 fDoc = null;
138 //
139 fMatchInput = null;
140 fPattern = null;
141 fReplace = null;
142 fButtonReplaceAll = null;
143 fButtonReplaceClose = null;
144 fButtonRelaceFind = null;
145 fButtonFind = null;
146 fButtonPrev = null;
147 fButtonNext = null;
148 fSingleLine = null;
149 fCaseSensitive = null;
150 fButtonClose = null;
151 }
152
153 void init(ITextEditor editor) {
154 if (DEBUG)
155 System.err.println("init");
156 fHistory.clear();
157 fButtonPrev.setEnabled(false);
158 isWrapped = false;
159 fEditor = editor;
160 if (fReEngine == null)
161 fReEngine = new Perl5Util();
162 //
163 fDoc = fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput());
164 ISelection s = fEditor.getSelectionProvider().getSelection();
165 int doclength = fDoc.getLength();
166 fStart = 0;
167 if (fEnd != null)
168 fDoc.removePosition(fEnd);
169 else
170 fEnd = new Position(0, 0);
171 fEnd.offset = doclength;
172 fOffset = 0;
173 fAnchor = fOffset;
174 String pat = null;
175 if (s instanceof TextSelection) {
176 TextSelection selection = (TextSelection) s;
177 fStart = selection.getOffset();
178 fEnd.offset = fStart + selection.getLength();
179 fOffset = fStart;
180 fAnchor = fOffset;
181 int sline;
182 int eline;
183 try {
184 sline = fDoc.getLineOfOffset(fStart) + 1;
185 eline = fDoc.getLineOfOffset(fEnd.offset) + 1;
186 } catch (BadLocationException e) {
187 JdtPlugin.log(NAME + ".init()", e);
188 sline = eline = 0;
189 }
190 if (selection.getLength() > 0) {
191 if (sline != eline) {
192 fStatus.setText("Search region: line " + sline + " to " + eline);
193 } else {
194 try {
195 pat = fDoc.get(fStart, fEnd.offset - fStart);
196 savePattern(pat);
197 } catch (BadLocationException e) {
198 JdtPlugin.log(NAME + ".init()", e);
199 }
200 fStart = 0;
201 fEnd.offset = doclength;
202 fStatus.setText("Search region: whole buffer, starting at line " + sline);
203 }
204 } else {
205 fStart = 0;
206 fEnd.offset = doclength;
207 fStatus.setText("Search region: whole buffer, starting at line " + sline);
208 }
209 }
210 fLastReplaced = -1;
211 fMatchInput = new PatternMatcherInput(fDoc.get(), fStart, fEnd.offset - fStart);
212 fMatchInput.setCurrentOffset(fOffset);
213 try {
214 fDoc.addPosition(fEnd);
215 } catch (BadLocationException e) {
216 JdtPlugin.log(NAME + ".init(): fEnd.offset=" + fEnd.offset, e);
217 }
218 if (pat != null) {
219 fPattern.removeModifyListener(fPatternChangedHandler);
220 setEnableFind(false);
221 if (fPatterns.size() > 0) {
222 fPattern.setItems((String[]) fPatterns.toArray(new String[fPatterns.size()]));
223 fPattern.setText((String) fPatterns.get(0));
224 setEnableFind(fPattern.getText().length() > 0);
225 }
226 fPattern.addModifyListener(fPatternChangedHandler);
227 }
228 fEditor.selectAndReveal(fOffset, 0);
229 }
230
231 ////////////////////////////////////////////////////////////////////////
232
233 /**
234 * @see org.eclipse.jface.window.Window#constrainShellSize()
235 */
236 protected void constrainShellSize() {
237 Shell shell = getShell();
238 if (fWindowPosition != null) {
239 System.err.println("Last Window location=" + fWindowPosition);
240 shell.setLocation(fWindowPosition);
241 } else {
242 fWindowPosition = shell.getLocation();
243 shell.setLocation(fWindowPosition.x - 400, fWindowPosition.y + 400);
244 }
245 super.constrainShellSize();
246 }
247
248 /**
249 * @see org.eclipse.jface.window.Window#createContents(Composite)
250 */
251 protected Control createContents(Composite parent) {
252 Composite top = UIUtil.createBox(parent, 1, 1, true, true, 1, 10, 10, 0, 10, null);
253 createDialogArea(top);
254 createButtonBar(top);
255 return top;
256 }
257
258 protected Control createDialogArea(Composite parent) {
259 ThemeManager manager = JdtPlugin.getThemeManager();
260 Font boldfont = manager.getFont("Bold");
261 Color lightcolor = manager.getColor("LightBG");
262 //
263 String title = JdtPlugin.getString("RegexFindReplace.title");
264 String str_search = JdtPlugin.getString("RegexFindReplace.search") + ": ";
265 String str_replace = JdtPlugin.getString("RegexFindReplace.replace") + ": ";
266 String str_oneline = JdtPlugin.getString("RegexFindReplace.oneline");
267 String str_casesensitive = JdtPlugin.getString("RegexFindReplace.case.sensitive");
268 String str_replaceall = JdtPlugin.getString("RegexFindReplace.replace.all");
269 String str_replacefind = JdtPlugin.getString("RegexFindReplace.replace.find");
270 String str_replaceclose = JdtPlugin.getString("RegexFindReplace.replace.close");
271 String str_find = JdtPlugin.getString("RegexFindReplace.find");
272 //
273 getShell().setText(title);
274 Composite top = UIUtil.createBox(parent, 1, 1, true, true, 3, 0, 0, 5, 5, true, null);
275 UIUtil.createGridLabel(
276 top,
277 SWT.RIGHT,
278 str_search,
279 null,
280 null,
281 boldfont,
282 1,
283 1,
284 GridData.FILL,
285 GridData.CENTER,
286 false,
287 false);
288 fPattern = new Combo(top, SWT.FLAT);
289 fPattern.setLayoutData(UIUtil.fillCell(1, 2, true, false));
290 GC gc = new GC(fPattern);
291 gc.setFont(fPattern.getFont());
292 FontMetrics fm = gc.getFontMetrics();
293 ((GridData) fPattern.getLayoutData()).widthHint = fm.getAverageCharWidth() * WIDTH;
294 fPattern.setBackground(lightcolor);
295 UIUtil.createGridLabel(
296 top,
297 SWT.RIGHT,
298 str_replace,
299 null,
300 null,
301 boldfont,
302 1,
303 1,
304 GridData.FILL,
305 GridData.CENTER,
306 false,
307 false);
308 fReplace = new Combo(top, SWT.FLAT);
309 fReplace.setLayoutData(UIUtil.fillCell(1, 2, true, false));
310 ((GridData) fReplace.getLayoutData()).widthHint = fm.getAverageCharWidth() * WIDTH;
311 fReplace.setBackground(lightcolor);
312 //
313 //For GTK
314 Composite options = UIUtil.createBox(top, 1, 3, true, false, 8, 0, 0, 5, 5, true, null);
315 // For Motif
316 // Composite options = UIUtil.createBox(top, 1, 3, true, false, 2, 0, 0, 0, 0, true, null);
317 fSingleLine = UIUtil.createButton(options, SWT.CHECK, str_oneline, null, null);
318 fSingleLine.setLayoutData(UIUtil.alignCell(1, 4, GridData.END, GridData.CENTER, false, false));
319 fCaseSensitive = UIUtil.createButton(options, SWT.CHECK, str_casesensitive, null, null);
320 fCaseSensitive.setLayoutData(UIUtil.alignCell(1, 4, GridData.END, GridData.CENTER, false, false));
321 //Composite buttons = UIUtil.createBox(top, 1, 4, true, false, 2, 0, 0, 0, 0, true, null);
322 fStatus = new Label(options, SWT.LEFT);
323 fStatus.setLayoutData(UIUtil.fillCell(2, 8, false, true));
324 fStatus.setForeground(manager.getColor("Red"));
325 ((GridData) fStatus.getLayoutData()).heightHint = fm.getHeight() * 2;
326 gc.dispose();
327 //
328 fButtonReplaceAll =
329 UIUtil.createButton(options, SWT.NONE, str_replaceall, lightcolor, null, 1, 4, true, false);
330 fButtonReplaceAll.addSelectionListener(new SelectionAdapter() {
331 public void widgetSelected(SelectionEvent e) {
332 actionReplaceAll();
333 }
334 });
335 fButtonRelaceFind =
336 UIUtil.createButton(options, SWT.NONE, str_replacefind, lightcolor, null, 1, 4, true, false);
337 fButtonRelaceFind.addSelectionListener(new SelectionAdapter() {
338 public void widgetSelected(SelectionEvent e) {
339 actionReplaceFind();
340 }
341
342 });
343 //
344 fButtonReplaceClose =
345 UIUtil.createButton(options, SWT.NONE, str_replaceclose, lightcolor, null, 1, 4, true, false);
346 fButtonReplaceClose.addSelectionListener(new SelectionAdapter() {
347 public void widgetSelected(SelectionEvent e) {
348 actionReplaceClose();
349 }
350 });
351 fButtonFind = UIUtil.createButton(options, SWT.NONE, str_find, lightcolor, null, 1, 3, true, false);
352 fButtonFind.addSelectionListener(new SelectionAdapter() {
353 public void widgetSelected(SelectionEvent e) {
354 actionFind();
355 }
356
357 });
358 fButtonPrev = UIUtil.createButton(options, SWT.NONE, "", lightcolor, null, 1, 1, true, false);
359 fButtonPrev.setToolTipText("Previous in history");
360 fButtonPrev.setImage(manager.getImage("UpArrow"));
361 fButtonPrev.setEnabled(false);
362 fButtonPrev.addSelectionListener(new SelectionAdapter() {
363 public void widgetSelected(SelectionEvent e) {
364 actionHistoryPrev();
365 }
366 });
367 //
368 fHistory.clear();
369 // init(fEditor);
370 //
371 setEnableFind(false);
372 if (fPatterns.size() > 0) {
373 fPattern.setItems((String[]) fPatterns.toArray(new String[fPatterns.size()]));
374 fPattern.setText((String) fPatterns.get(0));
375 setEnableFind(fPattern.getText().length() > 0);
376 }
377 setEnableReplace(false);
378 if (fReplaces.size() > 0) {
379 fReplace.setItems((String[]) fReplaces.toArray(new String[fReplaces.size()]));
380 fReplace.setText((String) fReplaces.get(0));
381 }
382 getShell().addDisposeListener(new DisposeListener() {
383 public void widgetDisposed(DisposeEvent e) {
384 dispose();
385 }
386 });
387 fPattern.addModifyListener(fPatternChangedHandler);
388 getShell().setDefaultButton(fButtonFind);
389 fPattern.setFocus();
390 //
391 getShell().addShellListener(new ShellAdapter() {
392 public void shellActivated(ShellEvent e) {
393 super.shellActivated(e);
394 init(getEditor());
395 }
396 public void shellDeactivated(ShellEvent e) {
397 super.shellDeactivated(e);
398 setEnableReplace(false);
399 setEnablePrev(false);
400 }
401 });
402 return top;
403 }
404
405 protected Control createButtonBar(Composite parent) {
406 Composite top = UIUtil.createBox(parent, 1, 1, 1, 0, 0, null);
407 fButtonClose = UIUtil.createButton(top, SWT.CENTER, "Close", null, null, 1, 1, true, false);
408 fButtonClose.addSelectionListener(new SelectionAdapter() {
409 public void widgetSelected(SelectionEvent e) {
410 actionClose();
411 }
412 });
413 return top;
414 }
415
416 public boolean close() {
417 if (DEBUG)
418 System.err.println("close()");
419 fWindowPosition = getShell().getLocation();
420 savePattern(fPattern.getText());
421 saveReplace(fReplace.getText());
422 fReEngine = null;
423 return super.close();
424 }
425
426 ////////////////////////////////////////////////////////////////////////
427
428 ITextEditor getEditor() {
429 return fEditor;
430 }
431
432 void setEnableFind(boolean enable) {
433 fButtonFind.setEnabled(enable);
434 }
435
436 void setEnableReplace(boolean enable) {
437 fButtonReplaceAll.setEnabled(enable);
438 fButtonReplaceClose.setEnabled(enable);
439 fButtonRelaceFind.setEnabled(enable);
440 }
441
442 void setEnablePrev(boolean b) {
443 fButtonPrev.setEnabled(b);
444 }
445
446 ////////////////////////////////////////////////////////////////////////
447
448 boolean find(String pat, String opt) {
449 if (isWrapped) {
450 fHistory.clear();
451 fButtonPrev.setEnabled(false);
452 isWrapped = false;
453 }
454 pat = Util.escapeRegex(pat);
455 fStatus.setText("");
456 try {
457 if (fReEngine.match("/" + pat + opt, fMatchInput)) {
458 //
459 fOffset = fReEngine.getMatch().beginOffset(0);
460 fAnchor = fOffset;
461 int len = fReEngine.getMatch().endOffset(0) - fOffset;
462 fEditor.selectAndReveal(fOffset, len);
463 setEnableReplace(true);
464 appendHistory(fOffset, len);
465 return true;
466 }
467 } catch (Exception e) {
468 fStatus.setText("ERROR: " + e.getMessage());
469 return false;
470 }
471 return wrap();
472 }
473
474 /**
475 * A simplified version of find() for actionReplaceAll to avoid accessing UI, eg. fStatus.
476 *
477 * @param pat
478 * @param opt
479 * @return boolean
480 */
481 boolean find1(String pat, String opt) {
482 pat = Util.escapeRegex(pat);
483 try {
484 if (fReEngine.match("/" + pat + opt, fMatchInput)) {
485 //
486 fOffset = fReEngine.getMatch().beginOffset(0);
487 fAnchor = fOffset;
488 return true;
489 }
490 } catch (Exception e) {
491 return false;
492 }
493 return false;
494 }
495
496 boolean replace(String pat, String replace, String opt, StringBuffer buf) {
497 String input = fReEngine.getMatch().group(0);
498 int start = fOffset;
499 int len = input.length();
500 String ret;
501 boolean noadjust = pat.startsWith("^");
502 pat = fixPatternForReplace(pat);
503 replace = escapeReplaceString(replace);
504 ret = fReEngine.substitute("s/" + pat + "/" + replace + opt, input);
505 if (buf == null) {
506 try {
507 fDoc.replace(start, len, ret);
508 } catch (BadLocationException e) {
509 fEditor.selectAndReveal(fOffset, 0);
510 setEnableReplace(false);
511 return false;
512 }
513 } else
514 buf.replace(start, start + len, ret);
515 len = ret.length();
516 fOffset = start + len;
517 if (!noadjust)
518 fOffset = adjustOffset(pat, buf);
519 fAnchor = fOffset;
520 return true;
521 }
522
523 private void appendHistory(int offset, int len) {
524 int size = fHistory.size();
525 if (size < 2 || fHistory.get(size - 2) != offset) {
526 fHistory.add(offset);
527 fHistory.add(len);
528 } else if (fHistory.get(size - 2) == offset) {
529 fHistory.set(size - 1, len);
530 }
531 fButtonPrev.setEnabled(fHistory.size() > 2);
532 }
533
534 ////////////////////////////////////////////////////////////////////////
535
536 void actionClose() {
537 if (fReplaceAllRunning) {
538 fReplaceAllRunning = false;
539 return;
540 }
541 if (fLastReplaced >= 0)
542 fEditor.selectAndReveal(fLastReplaced, 0);
543 okPressed();
544 }
545
546 void actionFind() {
547 String pat = fPattern.getText();
548 savePattern(pat);
549 saveReplace(fReplace.getText());
550 if (DEBUG)
551 System.err.println(
552 NAME
553 + ".actionFind(): pattern="
554 + fPattern.getText()
555 + ", replace="
556 + fReplace.getText());
557 if (!find(pat, getSearchOptions())) {
558 getShell().getDisplay().beep();
559 }
560 }
561
562 void actionReplaceFind() {
563 String pat = fPattern.getText();
564 String replace = fReplace.getText();
565 savePattern(pat);
566 saveReplace(replace);
567 if (DEBUG)
568 System.err.println(
569 NAME
570 + ".actionReplaceFind(): pattern="
571 + fPattern.getText()
572 + ", replace="
573 + fReplace.getText());
574 if (!replace(pat, replace, getSearchOptions(), null)) {
575 getShell().getDisplay().beep();
576 return;
577 }
578 fLastReplaced = fOffset;
579 fMatchInput.setInput(fDoc.get(), fStart, fEnd.offset - fStart);
580 fMatchInput.setCurrentOffset(fOffset);
581 int len = fHistory.size();
582 if (len > 0) {
583 fHistory.set(len - 2, fOffset);
584 fHistory.set(len - 1, -1);
585 }
586 if (!find(pat, getSearchOptions())) {
587 getShell().getDisplay().beep();
588 setEnableReplace(false);
589 } else {
590 fButtonPrev.setEnabled(fHistory.size() > 2);
591 setEnableReplace(fHistory.size() > 0 && fHistory.getLast() >= 0);
592 }
593 }
594
595 void actionReplaceClose() {
596 String pat = fPattern.getText();
597 String replace = fReplace.getText();
598 savePattern(pat);
599 saveReplace(replace);
600 if (DEBUG)
601 System.err.println(
602 NAME
603 + ".actionReplaceClose(): pattern="
604 + fPattern.getText()
605 + ", replace="
606 + fReplace.getText());
607 if (!replace(pat, replace, getSearchOptions(), null)) {
608 getShell().getDisplay().beep();
609 return;
610 }
611 fEditor.selectAndReveal(fOffset, 0);
612 fLastReplaced = -1;
613 actionClose();
614 }
615
616 void actionReplaceAll() {
617 StringBuffer b;
618 try {
619 b = new StringBuffer(fDoc.get(0, fEnd.offset));
620 } catch (BadLocationException e) {
621 JdtPlugin.log("Bad location: #1", e);
622 return;
623 }
624 final StringBuffer buf = b;
625 final int[] offset = new int[] { fOffset };
626 final int[] replaced = new int[] { 0 };
627 final String pat = fPattern.getText();
628 final String replace = fReplace.getText();
629 final String opt = getSearchOptions();
630 savePattern(pat);
631 saveReplace(replace);
632 if (DEBUG)
633 System.err.println(
634 NAME
635 + ".actionReplaceAll(): pattern="
636 + fPattern.getText()
637 + ", replace="
638 + fReplace.getText());
639 // try {
640 // new ProgressMonitorDialog(getShell()).run(true, true, new IRunnableWithProgress() {
641 // public void run(IProgressMonitor monitor)
642 // throws InvocationTargetException, InterruptedException {
643 // int total = 100;
644 // monitor.beginTask("Replace all ...", total);
645 Thread t = new Thread(new Runnable() {
646 public void run() {
647 int len;
648 do {
649 replace(pat, replace, opt, buf);
650 len = buf.length() - fStart;
651 fMatchInput.setInput(buf.toString(), fStart, len);
652 offset[0] = fOffset;
653 ++replaced[0];
654 fMatchInput.setCurrentOffset(fOffset);
655 // monitor.worked(total * (fOffset - fStart) / len);
656 } while (find1(pat, opt) && fOffset < buf.length() && fReplaceAllRunning);
657 getShell().getDisplay().asyncExec(new Runnable() {
658 public void run() {
659 replaceAllDone(buf, offset, replaced);
660 }
661 });
662 }
663 });
664 fReplaceAllRunning = true;
665 fButtonClose.setText("Stop replace ...");
666 t.start();
667 // monitor.done();
668 // }
669 // });
670 // } catch (InvocationTargetException e) {
671 // JdtPlugin.log("Replace failed", e);
672 // getShell().getDisplay().beep();
673 // } catch (InterruptedException e) {
674 // JdtPlugin.log("Replace aborted", e);
675 // getShell().getDisplay().beep();
676 // }
677 }
678
679 void replaceAllDone(final StringBuffer buf, final int[] offset, final int[] replaced) {
680 fReplaceAllRunning = false;
681 try {
682 fDoc.replace(fStart, fEnd.offset - fStart, buf.substring(fStart, buf.length()));
683 } catch (BadLocationException e) {
684 JdtPlugin.log("Bad location: #2", e);
685 getShell().getDisplay().beep();
686 return;
687 }
688 fEditor.selectAndReveal(offset[0], 0);
689 fStatus.setText("" + replaced[0] + " replaced.");
690 fButtonClose.setText("Close");
691 fLastReplaced = -1;
692 fHistory.clear();
693 wrap();
694 setEnableReplace(false);
695 //NOTE:
696 // Since the RegexReplaceDialog is deactivated when the progress dialog popup
697 // and activated when the progress dialog close. Thus input range would be reset to
698 // the whole buffer after each replaceall action.
699 }
700
701 void actionHistoryPrev() {
702 if (fHistory.size() < 4) {
703 getShell().getDisplay().beep();
704 return;
705 }
706 fHistory.decreaseLength(2);
707 int size = fHistory.size();
708 int offset = fHistory.get(size - 2);
709 int len = fHistory.get(size - 1);
710 setEnableReplace(len >= 0);
711 if (len < 0)
712 len = 0;
713 fButtonPrev.setEnabled(size > 2);
714 fEditor.selectAndReveal(offset, len);
715 fOffset = offset;
716 fAnchor = offset;
717 isWrapped = false;
718 fMatchInput.setCurrentOffset(offset + len);
719 }
720
721 String getSearchOptions() {
722 String opt = "/";
723 if (!fCaseSensitive.getSelection())
724 opt += 'i';
725 if (fSingleLine.getSelection())
726 opt += 's';
727 else
728 opt += 'm';
729 return opt;
730 }
731
732 void patternChanged(ModifyEvent event) {
733 String pat = fPattern.getText();
734 setEnableFind(pat.length() > 0);
735 fMatchInput.setCurrentOffset(fAnchor);
736 fStatus.setText("");
737 try {
738 if (pat.length() > 0 && fReEngine.match("/" + pat + getSearchOptions(), fMatchInput)) {
739 //
740 fOffset = fReEngine.getMatch().beginOffset(0);
741 int len = fReEngine.getMatch().endOffset(0) - fOffset;
742 fEditor.selectAndReveal(fOffset, len);
743 setEnableReplace(true);
744 appendHistory(fOffset, len);
745 return;
746 }
747 } catch (Exception e) {
748 fStatus.setText("ERROR: " + e.getMessage());
749 }
750 setEnableReplace(false);
751 }
752
753 ////////////////////////////////////////////////////////////////////////
754
755 private void savePattern(String pat) {
756 if (fPatterns.size() > 0 && pat.equals(fPatterns.get(0)))
757 return;
758 if (fPatterns.size() >= MAX_PERSISTENT)
759 fPatterns.remove(fPatterns.size() - 1);
760 fPatterns.add(0, pat);
761 }
762
763 private void saveReplace(String replace) {
764 if (fReplaces.size() > 0 && replace.equals(fReplaces.get(0)))
765 return;
766 if (fReplaces.size() >= MAX_PERSISTENT)
767 fReplaces.remove(fReplaces.size() - 1);
768 fReplaces.add(0, replace);
769 }
770
771 /**
772 * @return true if there is a valid match in history, else false.
773 */
774 private boolean wrap() {
775 boolean ret = false;
776 isWrapped = true;
777 int len = fHistory.size();
778 if (len > 0) {
779 int offset = fHistory.get(len - 2);
780 len = fHistory.get(len - 1);
781 if (len < 0)
782 len = 0;
783 fEditor.selectAndReveal(offset, len);
784 getShell().getDisplay().beep();
785 ret = true;
786 }
787 fMatchInput.setCurrentOffset(fStart);
788 return ret;
789 }
790
791 /**
792 * Adjust fOffset after replace. If 'pat' ends with '$' and current offset is at end of line,
793 * advance fOffset to next line.
794 *
795 * @param pat
796 * @return
797 */
798 int adjustOffset(String pat, StringBuffer buf) {
799 int start = fOffset;
800 if (pat.endsWith("$")) {
801 int end = (buf == null) ? fEnd.offset : buf.length();
802 char c;
803 while (start < end) {
804 if (buf == null) {
805 try {
806 c = fDoc.getChar(start);
807 } catch (BadLocationException e) {
808 e.printStackTrace();
809 return end;
810 }
811 } else
812 c = buf.charAt(start);
813 if (c == '\r' || c == '\n')
814 ++start;
815 else
816 break;
817 }
818 }
819 return start;
820 }
821
822 /**
823 * Since we perfrom replace on the matched string of the previous find action,
824 * the leading look behind and trailing look ahead are not included in the matched string.
825 * Have to adjust the pattern for that. Since the pattern is already matched, the
826 * zero-width pattern is not actually required, we just remove them from the pattern.
827 *
828 * NOTE: As of jarkata-oro-2.0.6, it do not support look behind yet.
829 *
830 * @param s The original search pattern.
831 * @return String The trimmed search pattern.
832 */
833 private String fixPatternForReplace(String s) {
834 StringBuffer ret = new StringBuffer();
835 if (s.length() > 0 && s.charAt(0) != '^')
836 ret.append('^');
837 char c, cc;
838 for (int i = 0; i < s.length(); ++i) {
839 c = s.charAt(i);
840 if (c == '\\') {
841 ret.append(c);
842 c = s.charAt(++i);
843 } else if (c == '/') {
844 ret.append('\\');
845 } else if (c == '(') {
846 cc = s.charAt(i + 1);
847 if (cc == '?') {
848 cc = s.charAt(i + 2);
849 if (cc == '=' || cc == '!' || cc == '<') {
850 i = skipTill(')', s, i);
851 continue;
852 }
853 }
854 }
855 ret.append(c);
856 }
857 return ret.toString();
858 }
859
860 private int skipTill(char delim, String s, int start) {
861 char c;
862 for (int i = start; i < s.length(); ++i) {
863 c = s.charAt(i);
864 if (c == '\\')
865 ++i;
866 else if (c == delim)
867 return i;
868 }
869 return s.length();
870 }
871
872 private String escapeReplaceString(String s) {
873 if (s == null)
874 return null;
875 StringBuffer ret = new StringBuffer();
876 char c;
877 for (int i = 0, len = s.length(); i < len; ++i) {
878 c = s.charAt(i);
879 if (c == '/')
880 ret.append("\\/");
881 else if (c == '\\' && i + 1 < len) {
882 c = s.charAt(i + 1);
883 if (c == 't') {
884 ret.append('\t');
885 ++i;
886 } else if (c == 'n') {
887 ret.append('\n');
888 ++i;
889 } else if (c == 'r') {
890 ret.append('\r');
891 ++i;
892 } else if (c == 'f') {
893 ret.append('\f');
894 ++i;
895 } else if (c == '\\') {
896 ret.append('\\');
897 ++i;
898 } else
899 ret.append('\\');
900 } else
901 ret.append(c);
902 }
903 return ret.toString();
904 }
905
906 ////////////////////////////////////////////////////////////////////////
907
908 private class PatternChanged implements ModifyListener {
909 public void modifyText(ModifyEvent event) {
910 patternChanged(event);
911 }
912 }
913
914 ////////////////////////////////////////////////////////////////////////
915
916 }