diff --git a/RegExpSupport/src/META-INF/RegExpPlugin.xml b/RegExpSupport/src/META-INF/RegExpPlugin.xml
index 54f6f622997a..10c622345617 100644
--- a/RegExpSupport/src/META-INF/RegExpPlugin.xml
+++ b/RegExpSupport/src/META-INF/RegExpPlugin.xml
@@ -9,5 +9,10 @@
+
+
+ org.intellij.plugins.intelliLang.inject.quickedit.CheckRegExpIntentionAction
+ Declaration
+
diff --git a/plugins/IntelliLang/src/intentionDescriptions/CheckRegExpIntentionAction/after.java.template b/plugins/IntelliLang/src/intentionDescriptions/CheckRegExpIntentionAction/after.java.template
new file mode 100644
index 000000000000..5fddae87c53a
--- /dev/null
+++ b/plugins/IntelliLang/src/intentionDescriptions/CheckRegExpIntentionAction/after.java.template
@@ -0,0 +1,3 @@
+public class RegExp {
+ Pattern PATTERN = Pattern.compile("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}");
+}
diff --git a/plugins/IntelliLang/src/intentionDescriptions/CheckRegExpIntentionAction/before.java.template b/plugins/IntelliLang/src/intentionDescriptions/CheckRegExpIntentionAction/before.java.template
new file mode 100644
index 000000000000..aefaacc64c48
--- /dev/null
+++ b/plugins/IntelliLang/src/intentionDescriptions/CheckRegExpIntentionAction/before.java.template
@@ -0,0 +1,3 @@
+public class RegExp {
+ Pattern PATTERN = Pattern.compile("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{3,4}");
+}
diff --git a/plugins/IntelliLang/src/intentionDescriptions/CheckRegExpIntentionAction/description.html b/plugins/IntelliLang/src/intentionDescriptions/CheckRegExpIntentionAction/description.html
new file mode 100644
index 000000000000..729358641801
--- /dev/null
+++ b/plugins/IntelliLang/src/intentionDescriptions/CheckRegExpIntentionAction/description.html
@@ -0,0 +1,5 @@
+
+
+This intention allows you to play with regular expression and check it for bugs.
+
+
diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/CheckRegExpForm.form b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/CheckRegExpForm.form
new file mode 100644
index 000000000000..0272a0f10158
--- /dev/null
+++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/CheckRegExpForm.form
@@ -0,0 +1,75 @@
+
+
diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/CheckRegExpForm.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/CheckRegExpForm.java
new file mode 100644
index 000000000000..3ceebc01d760
--- /dev/null
+++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/CheckRegExpForm.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.intellij.plugins.intelliLang.inject.quickedit;
+
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.event.DocumentAdapter;
+import com.intellij.openapi.editor.event.DocumentEvent;
+import com.intellij.openapi.fileTypes.PlainTextFileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.Balloon;
+import com.intellij.openapi.ui.popup.JBPopupAdapter;
+import com.intellij.openapi.ui.popup.LightweightWindowEvent;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.ui.BalloonImpl;
+import com.intellij.ui.EditorTextField;
+import com.intellij.util.Alarm;
+import org.intellij.lang.regexp.RegExpLanguage;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.util.regex.Pattern;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class CheckRegExpForm {
+ private static final String LAST_EDITED_REGEXP = "last.edited.regexp";
+ private Pair> myParams;
+
+ private EditorTextField mySampleText; //TODO[kb]: make it multiline
+
+ private EditorTextField myRegExp;
+ private JPanel myRootPanel;
+ private Ref myRef;
+ private Project myProject;
+
+
+ public CheckRegExpForm(Pair> params) {
+ myParams = params;
+ }
+
+ private void createUIComponents() {
+ PsiFile file = myParams.first;
+ myProject = file.getProject();
+ myRef = myParams.second;
+ Document document = PsiDocumentManager.getInstance(myProject).getDocument(file);
+
+ myRegExp = new EditorTextField(document, myProject, RegExpLanguage.INSTANCE.getAssociatedFileType());
+ final String sampleText = PropertiesComponent.getInstance(myProject).getValue(LAST_EDITED_REGEXP, "Sample Text");
+ mySampleText = new EditorTextField(sampleText, myProject, PlainTextFileType.INSTANCE);
+ myRootPanel = new JPanel(new BorderLayout()) {
+ @Override
+ public void addNotify() {
+ super.addNotify();
+ IdeFocusManager.getGlobalInstance().requestFocus(mySampleText, true);
+
+ final KeyAdapter escaper = new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+ if (!myRef.get().isDisposed()) {
+ myRef.get().hide();
+ }
+ }
+ }
+ };
+
+ final Editor fieldEditor = myRegExp.getEditor();
+ final Editor textEditor = mySampleText.getEditor();
+
+ assert fieldEditor != null && textEditor != null;
+
+ fieldEditor.getContentComponent().addKeyListener(escaper);
+ textEditor.getContentComponent().addKeyListener(escaper);
+
+ myRef.get().addListener(new JBPopupAdapter() {
+ @Override
+ public void onClosed(LightweightWindowEvent event) {
+ PropertiesComponent.getInstance(myProject).setValue(LAST_EDITED_REGEXP, mySampleText.getText());
+ }
+ });
+
+ final Alarm updater = new Alarm(Alarm.ThreadToUse.SWING_THREAD, myRef.get());
+ final DocumentAdapter documentListener = new DocumentAdapter() {
+ @Override
+ public void documentChanged(DocumentEvent e) {
+ updater.cancelAllRequests();
+ updater.addRequest(new Runnable() {
+ @Override
+ public void run() {
+ updateBalloon();
+ }
+ }, 200);
+ }
+ };
+ myRegExp.addDocumentListener(documentListener);
+ mySampleText.addDocumentListener(documentListener);
+
+ updateBalloon();
+ mySampleText.selectAll();
+ }
+ };
+ }
+
+
+ public JPanel getRootPanel() {
+ return myRootPanel;
+ }
+
+ private void updateBalloon() {
+ boolean correct = false;
+ try {
+ correct = Pattern.compile(myRegExp.getText()).matcher(mySampleText.getText()).matches();
+ } catch (Exception ignore) {}
+
+ mySampleText.setBackground(correct ? new Color(231, 250, 219) : new Color(255, 177, 160));
+ BalloonImpl balloon = (BalloonImpl)myRef.get();
+ if (balloon != null && balloon.isDisposed()) {
+ balloon.revalidate();
+ }
+ }
+}
diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/CheckRegExpIntentionAction.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/CheckRegExpIntentionAction.java
new file mode 100644
index 000000000000..64bdcbad25a7
--- /dev/null
+++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/CheckRegExpIntentionAction.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.intellij.plugins.intelliLang.inject.quickedit;
+
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.Balloon;
+import com.intellij.openapi.util.Iconable;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import org.intellij.lang.regexp.RegExpLanguage;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+/**
+ * @author Konstantin Bulenkov
+ */
+public class CheckRegExpIntentionAction extends QuickEditAction implements Iconable {
+ @Override
+ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
+ final Pair pair = getRangePair(file, editor);
+ /*super.isAvailable(project, editor, file) && */
+ return pair != null && pair.first != null && pair.first.getLanguage() == RegExpLanguage.INSTANCE;
+ }
+
+ @Override
+ protected boolean isShowInBalloon() {
+ return true;
+ }
+
+ @Override
+ protected JComponent createBalloonComponent(PsiFile file, final Ref ref) {
+ final Project project = file.getProject();
+ final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
+ if (document != null) {
+ return new CheckRegExpForm(new Pair>(file, ref)).getRootPanel();
+ }
+ return null;
+ }
+
+
+ @NotNull
+ @Override
+ public String getText() {
+ return "Check RegExp";
+ }
+
+ @NotNull
+ @Override
+ public String getFamilyName() {
+ return getText();
+ }
+
+ @Override
+ public Icon getIcon(int flags) {
+ //noinspection ConstantConditions
+ return RegExpLanguage.INSTANCE.getAssociatedFileType().getIcon();
+ }
+}
diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditAction.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditAction.java
index 0580a0487202..72a0c69ce0c1 100644
--- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditAction.java
+++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/quickedit/QuickEditAction.java
@@ -41,6 +41,8 @@ import com.intellij.openapi.fileEditor.impl.EditorWindow;
import com.intellij.openapi.fileEditor.impl.EditorWithProviderComposite;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.Balloon;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
@@ -50,13 +52,17 @@ import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.impl.source.tree.injected.Place;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.testFramework.LightVirtualFile;
+import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.Convertor;
+import com.intellij.util.ui.PositionTracker;
+import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
+import java.awt.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -67,27 +73,20 @@ import java.util.Set;
* fragment's complete prefix and suffix in non-editable areas and allows to edit the fragment
* without having to consider any additional escaping rules (e.g. when editing regexes in String
* literals).
+ *
+ * @author Gregory Shrago
+ * @author Konstantin Bulenkov
*/
public class QuickEditAction implements IntentionAction, LowPriorityAction {
-
private String myLastLanguageName;
- @NotNull
- public String getText() {
- return "Edit "+ StringUtil.notNullize(myLastLanguageName, "Injected")+" Fragment";
- }
-
- @NotNull
- public String getFamilyName() {
- return "Edit Injected Fragment";
- }
-
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
- return getRangePair(file, editor.getCaretModel().getOffset()) != null;
+ return getRangePair(file, editor) != null;
}
@Nullable
- private Pair getRangePair(final PsiFile file, final int offset) {
+ protected Pair getRangePair(final PsiFile file, final Editor editor) {
+ final int offset = editor.getCaretModel().getOffset();
final PsiLanguageInjectionHost host =
PsiTreeUtil.getParentOfType(file.findElementAt(offset), PsiLanguageInjectionHost.class, false);
if (host == null) return null;
@@ -107,7 +106,7 @@ public class QuickEditAction implements IntentionAction, LowPriorityAction {
public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) throws IncorrectOperationException {
final int offset = editor.getCaretModel().getOffset();
- final Pair pair = getRangePair(file, offset);
+ final Pair pair = getRangePair(file, editor);
assert pair != null;
final PsiFile injectedFile = (PsiFile)pair.first;
final int injectedOffset = ((DocumentWindow)PsiDocumentManager.getInstance(project).getDocument(injectedFile)).hostToInjected(offset);
@@ -119,20 +118,41 @@ public class QuickEditAction implements IntentionAction, LowPriorityAction {
}
private static final Key QUICK_EDIT_HANDLER = Key.create("QUICK_EDIT_HANDLER");
+
@NotNull
- private static MyHandler getHandler(Project project, PsiFile injectedFile, Editor editor, PsiFile origFile) {
+ private MyHandler getHandler(Project project, PsiFile injectedFile, Editor editor, PsiFile origFile) {
MyHandler handler = injectedFile.getUserData(QUICK_EDIT_HANDLER);
if (handler != null && handler.isValid()) {
return handler;
}
- injectedFile.putUserData(QUICK_EDIT_HANDLER, handler = new MyHandler(project, injectedFile, origFile, editor.getDocument()));
+ handler = new MyHandler(project, injectedFile, origFile, editor);
+ injectedFile.putUserData(QUICK_EDIT_HANDLER, handler);
return handler;
}
+
+ protected boolean isShowInBalloon() {
+ return false;
+ }
+
+ @Nullable
+ protected JComponent createBalloonComponent(PsiFile file, Ref ref) {
+ return null;
+ }
- private static class MyHandler extends DocumentAdapter implements Disposable {
+ @NotNull
+ public String getText() {
+ return "Edit "+ StringUtil.notNullize(myLastLanguageName, "Injected")+" Fragment";
+ }
+ @NotNull
+ public String getFamilyName() {
+ return "Edit Injected Fragment";
+ }
+
+ private class MyHandler extends DocumentAdapter implements Disposable {
private final Project myProject;
private final PsiFile myInjectedFile;
+ private final Editor myEditor;
private final Document myOrigDocument;
private final PsiFile myNewFile;
private final LightVirtualFile myNewVirtualFile;
@@ -142,10 +162,11 @@ public class QuickEditAction implements IntentionAction, LowPriorityAction {
private EditorWindow mySplittedWindow;
private boolean myReleased;
- private MyHandler(Project project, PsiFile injectedFile, final PsiFile origFile, Document origDocument) {
+ private MyHandler(Project project, PsiFile injectedFile, final PsiFile origFile, Editor editor) {
myProject = project;
myInjectedFile = injectedFile;
- myOrigDocument = origDocument;
+ myEditor = editor;
+ myOrigDocument = editor.getDocument();
final Place shreds = InjectedLanguageUtil.getShreds(myInjectedFile);
final FileType fileType = injectedFile.getFileType();
final Language language = injectedFile.getLanguage();
@@ -210,13 +231,42 @@ public class QuickEditAction implements IntentionAction, LowPriorityAction {
}
public void navigate(int injectedOffset) {
- final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(myProject);
- final FileEditor[] editors = fileEditorManager.getEditors(myNewVirtualFile);
- if (editors.length == 0) {
- final EditorWindow curWindow = fileEditorManager.getCurrentWindow();
- mySplittedWindow = curWindow.split(SwingConstants.HORIZONTAL, false, myNewVirtualFile, true);
+ if (isShowInBalloon()) {
+ Ref ref = Ref.create(null);
+ final JComponent component = createBalloonComponent(myNewFile, ref);
+ if (component != null) {
+ final Balloon balloon = JBPopupFactory.getInstance().createBalloonBuilder(component)
+ .setShadow(true)
+ .setAnimationCycle(0)
+ .setHideOnClickOutside(true)
+ .setHideOnKeyOutside(true)
+ .setHideOnAction(false)
+ .setFillColor(UIUtil.getControlColor())
+ .createBalloon();
+ ref.set(balloon);
+ Disposer.register(myNewFile.getProject(), balloon);
+ balloon.show(new PositionTracker(myEditor.getContentComponent()) {
+ @Override
+ public RelativePoint recalculateLocation(Balloon object) {
+ final RelativePoint target = JBPopupFactory.getInstance().guessBestPopupLocation(myEditor);
+ final Point screenPoint = target.getScreenPoint();
+ int y = screenPoint.y;
+ if (target.getPoint().getY() > myEditor.getLineHeight() + balloon.getPreferredSize().getHeight()) {
+ y -= myEditor.getLineHeight();
+ }
+ return new RelativePoint(new Point(screenPoint.x, y));
+ }
+ }, Balloon.Position.above);
+ }
+ } else {
+ final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(myProject);
+ final FileEditor[] editors = fileEditorManager.getEditors(myNewVirtualFile);
+ if (editors.length == 0) {
+ final EditorWindow curWindow = fileEditorManager.getCurrentWindow();
+ mySplittedWindow = curWindow.split(SwingConstants.HORIZONTAL, false, myNewVirtualFile, true);
+ }
+ fileEditorManager.openTextEditor(new OpenFileDescriptor(myProject, myNewVirtualFile, injectedOffset), true);
}
- fileEditorManager.openTextEditor(new OpenFileDescriptor(myProject, myNewVirtualFile, injectedOffset), true);
}
@Override