IDEA-25584
[fedora-idea.git] / platform / lang-impl / src / com / intellij / find / FindUtil.java
blob528993a0f4a204086eecc628a0bf50c48a63a2d2
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.intellij.find;
19 import com.intellij.codeInsight.hint.HintManager;
20 import com.intellij.codeInsight.hint.HintManagerImpl;
21 import com.intellij.codeInsight.hint.HintUtil;
22 import com.intellij.find.impl.FindInProjectUtil;
23 import com.intellij.find.replaceInProject.ReplaceInProjectManager;
24 import com.intellij.openapi.actionSystem.ActionManager;
25 import com.intellij.openapi.actionSystem.AnAction;
26 import com.intellij.openapi.actionSystem.IdeActions;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.command.CommandProcessor;
29 import com.intellij.openapi.editor.*;
30 import com.intellij.openapi.editor.actionSystem.EditorActionManager;
31 import com.intellij.openapi.editor.colors.EditorColors;
32 import com.intellij.openapi.editor.colors.EditorColorsManager;
33 import com.intellij.openapi.editor.event.CaretEvent;
34 import com.intellij.openapi.editor.event.CaretListener;
35 import com.intellij.openapi.editor.ex.DocumentEx;
36 import com.intellij.openapi.editor.ex.RangeHighlighterEx;
37 import com.intellij.openapi.editor.markup.HighlighterLayer;
38 import com.intellij.openapi.editor.markup.HighlighterTargetArea;
39 import com.intellij.openapi.editor.markup.RangeHighlighter;
40 import com.intellij.openapi.editor.markup.TextAttributes;
41 import com.intellij.openapi.fileEditor.FileDocumentManager;
42 import com.intellij.openapi.fileEditor.FileEditor;
43 import com.intellij.openapi.fileEditor.TextEditor;
44 import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
45 import com.intellij.openapi.keymap.KeymapUtil;
46 import com.intellij.openapi.project.Project;
47 import com.intellij.openapi.util.Key;
48 import com.intellij.openapi.util.Pair;
49 import com.intellij.openapi.util.TextRange;
50 import com.intellij.openapi.util.text.StringUtil;
51 import com.intellij.openapi.vfs.VirtualFile;
52 import com.intellij.psi.PsiDocumentManager;
53 import com.intellij.psi.PsiFile;
54 import com.intellij.ui.LightweightHint;
55 import com.intellij.usageView.UsageInfo;
56 import com.intellij.usages.*;
57 import org.jetbrains.annotations.NotNull;
58 import org.jetbrains.annotations.Nullable;
60 import javax.swing.*;
61 import java.awt.event.FocusAdapter;
62 import java.util.ArrayList;
63 import java.util.Collections;
64 import java.util.Comparator;
65 import java.util.List;
67 /**
70 public class FindUtil {
71 private static final Key<Direction> KEY = Key.create("FindUtil.KEY");
73 private FindUtil() {}
75 @Nullable static VirtualFile getVirtualFile(@NotNull Editor myEditor) {
76 Project project = myEditor.getProject();
77 PsiFile file = project != null ? PsiDocumentManager.getInstance(project).getPsiFile(myEditor.getDocument()):null;
78 return file != null ? file.getVirtualFile() : null;
81 private enum Direction {
82 UP, DOWN
85 public static void findWordAtCaret(Project project, Editor editor) {
86 int caretOffset = editor.getCaretModel().getOffset();
87 Document document = editor.getDocument();
88 CharSequence text = document.getCharsSequence();
89 int start = 0;
90 int end = document.getTextLength();
91 if (!editor.getSelectionModel().hasSelection()) {
92 for (int i = caretOffset - 1; i >= 0; i--) {
93 char c = text.charAt(i);
94 if (!Character.isJavaIdentifierPart(c)) {
95 start = i + 1;
96 break;
99 for (int i = caretOffset; i < document.getTextLength(); i++) {
100 char c = text.charAt(i);
101 if (!Character.isJavaIdentifierPart(c)) {
102 end = i;
103 break;
107 else {
108 start = editor.getSelectionModel().getSelectionStart();
109 end = editor.getSelectionModel().getSelectionEnd();
111 if (start >= end) {
112 return;
114 FindManager findManager = FindManager.getInstance(project);
115 String s = text.subSequence(start, end).toString();
116 FindSettings.getInstance().addStringToFind(s);
117 findManager.getFindInFileModel().setStringToFind(s);
118 findManager.setFindWasPerformed();
119 FindModel model = new FindModel();
120 model.setStringToFind(s);
121 model.setCaseSensitive(true);
122 model.setWholeWordsOnly(!editor.getSelectionModel().hasSelection());
124 final JComponent header = editor.getHeaderComponent();
125 if (header instanceof EditorSearchComponent) {
126 final EditorSearchComponent searchComponent = (EditorSearchComponent)header;
127 searchComponent.setTextInField(model.getStringToFind());
130 findManager.setFindNextModel(model);
131 doSearch(project, editor, caretOffset, true, model, true);
134 public static void find(final Project project, final Editor editor) {
135 ApplicationManager.getApplication().assertIsDispatchThread();
136 final FindManager findManager = FindManager.getInstance(project);
137 String s = editor.getSelectionModel().getSelectedText();
139 final FindModel model = (FindModel)findManager.getFindInFileModel().clone();
140 if (s != null) {
141 if (s.indexOf('\n') >= 0) {
142 model.setGlobal(false);
144 else {
145 model.setStringToFind(s);
146 model.setGlobal(true);
149 else {
150 model.setGlobal(true);
153 model.setReplaceState(false);
154 model.setFindAllEnabled(PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()) != null);
156 findManager.showFindDialog(model, new Runnable() {
157 public void run() {
158 if (model.isFindAll()) {
159 findManager.setFindNextModel(model);
160 findAll(project, editor, model);
161 return;
164 if (!model.isGlobal() && editor.getSelectionModel().hasSelection()) {
165 int offset = model.isForward()
166 ? editor.getSelectionModel().getSelectionStart()
167 : editor.getSelectionModel().getSelectionEnd();
168 ScrollType scrollType = model.isForward() ? ScrollType.CENTER_DOWN : ScrollType.CENTER_UP;
169 moveCaretAndDontChangeSelection(editor, offset, scrollType);
172 int offset;
173 if (model.isGlobal()) {
174 if (model.isFromCursor()) {
175 offset = editor.getCaretModel().getOffset();
177 else {
178 offset = model.isForward() ? 0 : editor.getDocument().getTextLength();
181 else {
182 // in selection
184 if (!editor.getSelectionModel().hasSelection()) {
185 // TODO[anton] actually, this should never happen - Find dialog should not allow such combination
186 findManager.setFindNextModel(null);
187 return;
190 offset = model.isForward() ? editor.getSelectionModel().getSelectionStart() : editor.getSelectionModel().getSelectionEnd();
193 findManager.setFindNextModel(null);
194 findManager.getFindInFileModel().copyFrom(model);
195 doSearch(project, editor, offset, true, model, true);
200 public static void findAll(final Project project, final Editor editor, final FindModel findModel) {
201 final Document document = editor.getDocument();
202 final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
203 if (psiFile == null) return;
205 CharSequence text = document.getCharsSequence();
206 int textLength = document.getTextLength();
207 final List<Usage> usages = new ArrayList<Usage>();
208 FindManager findManager = FindManager.getInstance(project);
209 findModel.setForward(true); // when find all there is no diff in direction
211 int offset = 0;
212 VirtualFile virtualFile = getVirtualFile(editor);
214 while (offset < textLength) {
215 FindResult result = findManager.findString(text, offset, findModel, virtualFile);
216 if (!result.isStringFound()) break;
218 usages.add(new UsageInfo2UsageAdapter(new UsageInfo(psiFile, result.getStartOffset(), result.getEndOffset())));
220 final int prevOffset = offset;
221 offset = result.getEndOffset();
223 if (prevOffset == offset) {
224 // for regular expr the size of the match could be zero -> could be infinite loop in finding usages!
225 ++offset;
228 final UsageTarget[] usageTargets = { new FindInProjectUtil.StringUsageTarget(findModel.getStringToFind()) };
229 final UsageViewPresentation usageViewPresentation = FindInProjectUtil.setupViewPresentation(false, findModel);
230 UsageViewManager.getInstance(project).showUsages(usageTargets, usages.toArray(new Usage[usages.size()]), usageViewPresentation);
233 public static void searchBack(Project project, FileEditor fileEditor) {
234 if (!(fileEditor instanceof TextEditor)) return;
235 TextEditor textEditor = (TextEditor)fileEditor;
236 Editor editor = textEditor.getEditor();
238 searchBack(project, editor);
241 public static void searchBack(final Project project, final Editor editor) {
242 FindManager findManager = FindManager.getInstance(project);
243 if (!findManager.findWasPerformed()) {
244 find(project, editor);
245 return;
248 FindModel model = findManager.getFindNextModel(editor);
249 if (model == null) {
250 model = findManager.getFindInFileModel();
252 model = (FindModel)model.clone();
253 model.setForward(!model.isForward());
254 if (!model.isGlobal() && !editor.getSelectionModel().hasSelection()) {
255 model.setGlobal(true);
258 int offset;
259 if (Direction.UP.equals(editor.getUserData(KEY)) && !model.isForward()) {
260 offset = editor.getDocument().getTextLength();
262 else if (Direction.DOWN.equals(editor.getUserData(KEY)) && model.isForward()) {
263 offset = 0;
265 else {
266 editor.putUserData(KEY, null);
267 offset = editor.getCaretModel().getOffset();
268 if (!model.isForward() && offset > 0) {
269 offset--;
272 searchAgain(project, editor, offset, model);
275 public static boolean searchAgain(Project project, FileEditor fileEditor) {
276 if (!(fileEditor instanceof TextEditor)) return false;
277 TextEditor textEditor = (TextEditor)fileEditor;
278 Editor editor = textEditor.getEditor();
280 return searchAgain(project, editor);
283 public static boolean searchAgain(final Project project, final Editor editor) {
284 FindManager findManager = FindManager.getInstance(project);
285 if (!findManager.findWasPerformed()) {
286 find(project, editor);
287 return false;
290 FindModel model = findManager.getFindNextModel(editor);
291 if (model == null) {
292 model = findManager.getFindInFileModel();
294 model = (FindModel)model.clone();
296 int offset;
297 if (Direction.DOWN.equals(editor.getUserData(KEY)) && model.isForward()) {
298 offset = 0;
300 else if (Direction.UP.equals(editor.getUserData(KEY)) && !model.isForward()) {
301 offset = editor.getDocument().getTextLength();
303 else {
304 editor.putUserData(KEY, null);
305 offset = editor.getCaretModel().getOffset();
306 if (!model.isForward() && offset > 0 ) {
307 offset--;
310 return searchAgain(project, editor, offset, model);
313 private static boolean searchAgain(Project project, Editor editor, int offset, FindModel model) {
314 if (!model.isGlobal() && !editor.getSelectionModel().hasSelection()) {
315 model.setGlobal(true);
317 model.setFromCursor(false);
318 if (model.isReplaceState()) {
319 model.setPromptOnReplace(true);
320 model.setReplaceAll(false);
321 replace(project, editor, offset, model);
322 return true;
324 else {
325 doSearch(project, editor, offset, true, model, true);
326 return false;
330 public static void replace(final Project project, final Editor editor) {
331 final FindManager findManager = FindManager.getInstance(project);
332 final FindModel model = (FindModel)findManager.getFindInFileModel().clone();
333 final String s = editor.getSelectionModel().getSelectedText();
334 if (s != null) {
335 if (s.indexOf('\n') >= 0) {
336 model.setGlobal(false);
338 else {
339 model.setStringToFind(s);
340 model.setGlobal(true);
343 else {
344 model.setGlobal(true);
346 model.setReplaceState(true);
348 findManager.showFindDialog(model, new Runnable() {
349 public void run() {
350 if (!model.isGlobal() && editor.getSelectionModel().hasSelection()) {
351 int offset = model.isForward()
352 ? editor.getSelectionModel().getSelectionStart()
353 : editor.getSelectionModel().getSelectionEnd();
354 ScrollType scrollType = model.isForward() ? ScrollType.CENTER_DOWN : ScrollType.CENTER_UP;
355 moveCaretAndDontChangeSelection(editor, offset, scrollType);
357 int offset;
358 if (model.isGlobal()) {
359 if (model.isFromCursor()) {
360 offset = editor.getCaretModel().getOffset();
361 if (!model.isForward()) {
362 offset++;
365 else {
366 offset = model.isForward() ? 0 : editor.getDocument().getTextLength();
369 else {
370 // in selection
372 if (!editor.getSelectionModel().hasSelection()) {
373 // TODO[anton] actually, this should never happen - Find dialog should not allow such combination
374 findManager.setFindNextModel(null);
375 return;
378 offset = model.isForward() ? editor.getSelectionModel().getSelectionStart() : editor.getSelectionModel().getSelectionEnd();
381 if (s != null && editor.getSelectionModel().hasSelection() && s.equals(model.getStringToFind())) {
382 if (model.isFromCursor() && model.isForward()) {
383 offset = Math.min(editor.getSelectionModel().getSelectionStart(), offset);
385 else if (model.isFromCursor() && !model.isForward()) {
386 offset = Math.max(editor.getSelectionModel().getSelectionEnd(), offset);
389 findManager.setFindNextModel(null);
390 findManager.getFindInFileModel().copyFrom(model);
391 replace(project, editor, offset, model);
396 public static boolean replace(Project project, Editor editor, int offset, FindModel model) {
397 Document document = editor.getDocument();
399 if (!FileDocumentManager.getInstance().requestWriting(document, project)) {
400 return false;
403 document.startGuardedBlockChecking();
404 boolean toPrompt = model.isPromptOnReplace();
406 if (!toPrompt) {
407 ((DocumentEx) document).setInBulkUpdate(true);
409 try {
410 toPrompt = doReplace(project, editor, model, document, offset, toPrompt);
412 catch (ReadOnlyFragmentModificationException e) {
413 EditorActionManager.getInstance().getReadonlyFragmentModificationHandler(document).handle(e);
415 finally {
416 if (!toPrompt) {
417 ((DocumentEx) document).setInBulkUpdate(false);
419 document.stopGuardedBlockChecking();
422 return true;
425 private static boolean doReplace(Project project, Editor editor, FindModel model, final Document document, int caretOffset, boolean toPrompt) {
426 FindManager findManager = FindManager.getInstance(project);
427 model = (FindModel)model.clone();
428 int occurrences = 0;
430 List<Pair<TextRange,String>> rangesToChange = new ArrayList<Pair<TextRange, String>>();
432 boolean replaced = false;
433 int offset = caretOffset;
434 while (offset >= 0 && offset < editor.getDocument().getTextLength()) {
435 caretOffset = offset;
436 FindResult result = doSearch(project, editor, offset, !replaced, model, toPrompt);
437 if (result == null) {
438 break;
440 int startResultOffset = result.getStartOffset();
441 model.setFromCursor(true);
442 if (toPrompt) {
443 int promptResult = findManager.showPromptDialog(model, FindBundle.message("find.replace.dialog.title"));
444 if (promptResult == FindManager.PromptResult.SKIP) {
445 offset = model.isForward() ? result.getEndOffset() : startResultOffset;
446 continue;
448 if (promptResult == FindManager.PromptResult.CANCEL) {
449 break;
451 if (promptResult == FindManager.PromptResult.ALL) {
452 toPrompt = false;
453 ((DocumentEx) document).setInBulkUpdate(true);
457 int startOffset = result.getStartOffset();
458 int endOffset = result.getEndOffset();
459 String foundString = document.getCharsSequence().subSequence(startOffset, endOffset).toString();
460 String toReplace = findManager.getStringToReplace(foundString, model, startOffset, document.getText());
461 if (toReplace == null) break;
463 boolean reallyReplace = toPrompt;
464 TextRange textRange = doReplace(project, document, model, result, toReplace, reallyReplace, rangesToChange);
466 int newOffset = model.isForward() ? textRange.getEndOffset() : textRange.getStartOffset();
467 if (newOffset == offset) {
468 newOffset += model.isForward() ? 1 : -1;
470 offset = newOffset;
471 occurrences++;
473 //[SCR 7258]
474 if (!replaced) {
475 editor.getCaretModel().moveToOffset(0);
478 replaced = true;
481 if (replaced) {
482 if (!toPrompt) {
483 CharSequence text = document.getCharsSequence();
484 final StringBuilder newText = new StringBuilder(document.getTextLength());
485 Collections.sort(rangesToChange, new Comparator<Pair<TextRange, String>>() {
486 public int compare(Pair<TextRange, String> o1, Pair<TextRange, String> o2) {
487 return o1.getFirst().getStartOffset() - o2.getFirst().getStartOffset();
490 int offsetBefore = 0;
491 for (Pair<TextRange, String> pair : rangesToChange) {
492 TextRange range = pair.getFirst();
493 String replace = pair.getSecond();
494 newText.append(text, offsetBefore, range.getStartOffset()); //before change
495 newText.append(replace);
496 offsetBefore = range.getEndOffset();
497 if (offsetBefore < caretOffset) {
498 caretOffset += replace.length() - range.getLength();
501 newText.append(text, offsetBefore, text.length()); //tail
502 CommandProcessor.getInstance().executeCommand(project, new Runnable() {
503 public void run() {
504 ApplicationManager.getApplication().runWriteAction(new Runnable() {
505 public void run() {
506 document.setText(newText);
510 }, null, document);
512 if (caretOffset > document.getTextLength()) caretOffset = document.getTextLength();
514 editor.getCaretModel().moveToOffset(caretOffset);
517 ReplaceInProjectManager.reportNumberReplacedOccurences(project, occurrences);
518 return replaced;
521 @Nullable
522 private static FindResult doSearch(Project project,
523 final Editor editor,
524 int offset,
525 boolean toWarn,
526 FindModel model, boolean adjustEditor) {
527 FindManager findManager = FindManager.getInstance(project);
528 Document document = editor.getDocument();
530 final FindResult result = findManager.findString(document.getCharsSequence(), offset, model, getVirtualFile(editor));
532 boolean isFound = result.isStringFound();
533 if (!model.isGlobal()) {
534 if (result.getEndOffset() > editor.getSelectionModel().getSelectionEnd() ||
535 result.getStartOffset() < editor.getSelectionModel().getSelectionStart()) {
536 isFound = false;
539 if (!isFound) {
540 if (toWarn) {
541 processNotFound(editor, model.getStringToFind(), model, project);
543 return null;
546 if (adjustEditor) {
547 final CaretModel caretModel = editor.getCaretModel();
548 final ScrollingModel scrollingModel = editor.getScrollingModel();
549 int oldCaretOffset = caretModel.getOffset();
550 boolean forward = oldCaretOffset < result.getStartOffset();
551 final ScrollType scrollType = forward ? ScrollType.CENTER_DOWN : ScrollType.CENTER_UP;
553 if (model.isGlobal()) {
554 caretModel.moveToOffset(result.getEndOffset());
555 editor.getSelectionModel().removeSelection();
556 scrollingModel.scrollToCaret(scrollType);
557 scrollingModel.runActionOnScrollingFinished(
558 new Runnable() {
559 public void run() {
560 scrollingModel.scrollTo(editor.offsetToLogicalPosition(result.getStartOffset()), scrollType);
561 scrollingModel.scrollTo(editor.offsetToLogicalPosition(result.getEndOffset()), scrollType);
566 else {
567 moveCaretAndDontChangeSelection(editor, result.getStartOffset(), scrollType);
568 moveCaretAndDontChangeSelection(editor, result.getEndOffset(), scrollType);
570 IdeDocumentHistory.getInstance(project).includeCurrentCommandAsNavigation();
572 EditorColorsManager manager = EditorColorsManager.getInstance();
573 TextAttributes selectionAttributes = manager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
575 if (!model.isGlobal()) {
576 final RangeHighlighterEx segmentHighlighter = (RangeHighlighterEx)editor.getMarkupModel().addRangeHighlighter(
577 result.getStartOffset(),
578 result.getEndOffset(),
579 HighlighterLayer.SELECTION + 1,
580 selectionAttributes, HighlighterTargetArea.EXACT_RANGE);
581 MyListener listener = new MyListener(editor, segmentHighlighter);
582 editor.getContentComponent().addFocusListener(listener);
583 caretModel.addCaretListener(listener);
585 else {
586 editor.getSelectionModel().setSelection(result.getStartOffset(), result.getEndOffset());
590 return result;
593 private static class MyListener extends FocusAdapter implements CaretListener {
594 private final Editor myEditor;
595 private final RangeHighlighter mySegmentHighlighter;
597 private MyListener(Editor editor, RangeHighlighter segmentHighlighter) {
598 myEditor = editor;
599 mySegmentHighlighter = segmentHighlighter;
602 public void caretPositionChanged(CaretEvent e) {
603 removeAll();
606 private void removeAll() {
607 myEditor.getMarkupModel().removeHighlighter(mySegmentHighlighter);
608 myEditor.getContentComponent().addFocusListener(this);
609 myEditor.getCaretModel().removeCaretListener(this);
613 private static void processNotFound(final Editor editor, String stringToFind, FindModel model, Project project) {
615 String message = FindBundle.message("find.search.string.not.found.message", stringToFind);
617 if (model.isGlobal()) {
618 final FindModel newModel = (FindModel)model.clone();
619 FindManager findManager = FindManager.getInstance(project);
620 Document document = editor.getDocument();
621 FindResult result;
622 if (newModel.isForward()) {
623 result = findManager.findString(document.getCharsSequence(), 0, model, getVirtualFile(editor));
625 else {
626 result = findManager.findString(document.getCharsSequence(), document.getTextLength(), model, getVirtualFile(editor));
628 if (!result.isStringFound()) {
629 result = null;
632 FindModel modelForNextSearch = findManager.getFindNextModel(editor);
633 if (modelForNextSearch == null) {
634 modelForNextSearch = findManager.getFindInFileModel();
637 if (result != null) {
638 if (newModel.isForward()) {
639 AnAction action = ActionManager.getInstance().getAction(
640 modelForNextSearch.isForward() ? IdeActions.ACTION_FIND_NEXT : IdeActions.ACTION_FIND_PREVIOUS);
641 String shortcutsText = KeymapUtil.getFirstKeyboardShortcutText(action);
642 if (shortcutsText.length() > 0) {
643 message = FindBundle.message("find.search.again.from.top.hotkey.message", message, shortcutsText);
645 else {
646 message = FindBundle.message("find.search.again.from.top.action.message", message);
648 editor.putUserData(KEY, Direction.DOWN);
650 else {
651 AnAction action = ActionManager.getInstance().getAction(
652 modelForNextSearch.isForward() ? IdeActions.ACTION_FIND_PREVIOUS : IdeActions.ACTION_FIND_NEXT);
653 String shortcutsText = KeymapUtil.getFirstKeyboardShortcutText(action);
654 if (shortcutsText.length() > 0) {
655 message = FindBundle.message("find.search.again.from.bottom.hotkey.message", message, shortcutsText);
657 else {
658 message = FindBundle.message("find.search.again.from.bottom.action.message", message);
660 editor.putUserData(KEY, Direction.UP);
663 CaretListener listener = new CaretListener() {
664 public void caretPositionChanged(CaretEvent e) {
665 editor.putUserData(KEY, null);
666 editor.getCaretModel().removeCaretListener(this);
669 editor.getCaretModel().addCaretListener(listener);
671 JComponent component = HintUtil.createInformationLabel(message);
672 final LightweightHint hint = new LightweightHint(component);
673 HintManagerImpl.getInstanceImpl().showEditorHint(hint, editor, HintManager.UNDER,
674 HintManager.HIDE_BY_ANY_KEY | HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING,
675 0, false);
678 private static TextRange doReplace(final Project project, final Document document, final FindModel model, FindResult result, @NotNull String stringToReplace,
679 boolean reallyReplace,
680 List<Pair<TextRange, String>> rangesToChange) {
681 final int startOffset = result.getStartOffset();
682 final int endOffset = result.getEndOffset();
684 final String converted = StringUtil.convertLineSeparators(stringToReplace);
685 int newOffset;
686 if (reallyReplace) {
687 CommandProcessor.getInstance().executeCommand(project, new Runnable() {
688 public void run() {
689 ApplicationManager.getApplication().runWriteAction(new Runnable() {
690 public void run() {
691 //[ven] I doubt converting is a good solution to SCR 21224
692 document.replaceString(startOffset, endOffset, converted);
696 }, null, document);
697 newOffset = startOffset + converted.length();
699 else {
700 TextRange textRange = new TextRange(startOffset, endOffset);
701 rangesToChange.add(Pair.create(textRange,converted));
703 newOffset = endOffset;
706 int start = startOffset;
707 int end = newOffset;
708 if (model.isRegularExpressions()) {
709 String toFind = model.getStringToFind();
710 if (model.isForward()) {
711 if (StringUtil.endsWithChar(toFind, '$')) {
712 int i = 0;
713 int length = toFind.length();
714 while (i + 2 <= length && toFind.charAt(length - i - 2) == '\\') i++;
715 if (i % 2 == 0) end++; //This $ is a special symbol in regexp syntax
717 else if (StringUtil.startsWithChar(toFind, '^')) {
718 while (end < document.getTextLength() && document.getCharsSequence().charAt(end) != '\n') end++;
721 else {
722 if (StringUtil.startsWithChar(toFind, '^')) {
723 start--;
725 else if (StringUtil.endsWithChar(toFind, '$')) {
726 while (start >= 0 && document.getCharsSequence().charAt(start) != '\n') start--;
730 return new TextRange(start, end);
733 private static void moveCaretAndDontChangeSelection(final Editor editor, int offset, ScrollType scrollType) {
734 LogicalPosition pos = editor.offsetToLogicalPosition(offset);
735 editor.getCaretModel().moveToLogicalPosition(pos);
736 editor.getScrollingModel().scrollToCaret(scrollType);