refactoring, zen coding shouldn't block live templates
authorEugene Kudelevsky <Eugene.Kudelevsky@jetbrains.com>
Mon, 1 Mar 2010 16:49:29 +0000 (19:49 +0300)
committerEugene Kudelevsky <Eugene.Kudelevsky@jetbrains.com>
Mon, 1 Mar 2010 16:49:29 +0000 (19:49 +0300)
platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java

index c6b6107098243298d55d27911ff05d6aeeeeec9b..40bcb468401be305022c14f2d88de8f88fe1869d 100644 (file)
@@ -220,40 +220,81 @@ public class TemplateManagerImpl extends TemplateManager implements ProjectCompo
     }
   }
 
+  private static boolean containsTemplateStartingBefore(Map<TemplateImpl, String> template2argument,
+                                                        int offset,
+                                                        int caretOffset,
+                                                        CharSequence text) {
+    for (TemplateImpl template : template2argument.keySet()) {
+      String argument = template2argument.get(template);
+      int templateStart = getTemplateStart(template, argument, caretOffset, text);
+      if (templateStart <= offset) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   public boolean startTemplate(final Editor editor, char shortcutChar, final PairProcessor<String, String> processor) {
     PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, myProject);
     if (file == null) return false;
     TemplateSettings templateSettings = TemplateSettings.getInstance();
+
+    Map<TemplateImpl, String> template2argument = findMatchingTemplates(file, editor, shortcutChar, templateSettings);
+
     if (shortcutChar == templateSettings.getDefaultShortcutChar()) {
       for (final CustomLiveTemplate customLiveTemplate : CustomLiveTemplate.EP_NAME.getExtensions()) {
         final CustomTemplateCallback callback = new CustomTemplateCallback(editor, file);
         callback.fixInitialEditorState();
         String key = customLiveTemplate.computeTemplateKey(callback);
         if (key != null) {
-          int offset = callback.getEditor().getCaretModel().getOffset();
-          callback.getEditor().getDocument().deleteString(offset - key.length(), offset);
-          customLiveTemplate.execute(key, callback, new TemplateInvokationListener() {
-            public void finished(boolean inSeparateEvent) {
-              callback.finish();
-            }
-          });
-          return true;
+          int caretOffset = editor.getCaretModel().getOffset();
+          int offsetBeforeKey = caretOffset - key.length();
+          CharSequence text = editor.getDocument().getCharsSequence();
+          if (template2argument == null || !containsTemplateStartingBefore(template2argument, offsetBeforeKey, caretOffset, text)) {
+            callback.getEditor().getDocument().deleteString(offsetBeforeKey, caretOffset);
+            customLiveTemplate.execute(key, callback, new TemplateInvokationListener() {
+              public void finished(boolean inSeparateEvent) {
+                callback.finish();
+              }
+            });
+            return true;
+          }
         }
       }
     }
-    return startNonCustomTemplate(templateSettings, file, editor, shortcutChar, processor);
+    return startNonCustomTemplates(template2argument, editor, processor);
   }
 
-  private boolean startNonCustomTemplate(TemplateSettings templateSettings,
-                                         PsiFile file,
-                                         Editor editor,
-                                         char shortcutChar,
-                                         PairProcessor<String, String> processor) {
-    final Document document = editor.getDocument();
+  private static int getArgumentOffset(int caretOffset, String argument, CharSequence text) {
+    int argumentOffset = caretOffset - argument.length();
+    if (argumentOffset > 0 && text.charAt(argumentOffset - 1) == ' ') {
+      if (argumentOffset - 2 >= 0 && Character.isJavaIdentifierPart(text.charAt(argumentOffset - 2))) {
+        argumentOffset--;
+      }
+    }
+    return argumentOffset;
+  }
 
-    CharSequence text = document.getCharsSequence();
+  private static int getTemplateStart(TemplateImpl template, String argument, int caretOffset, CharSequence text) {
+    int templateStart;
+    if (argument == null) {
+      templateStart = caretOffset - template.getKey().length();
+    }
+    else {
+      int argOffset = getArgumentOffset(caretOffset, argument, text);
+      templateStart = argOffset - template.getKey().length();
+    }
+    return templateStart;
+  }
 
+  private Map<TemplateImpl, String> findMatchingTemplates(final PsiFile file,
+                                                          Editor editor,
+                                                          char shortcutChar,
+                                                          TemplateSettings templateSettings) {
+    final Document document = editor.getDocument();
+    CharSequence text = document.getCharsSequence();
     final int caretOffset = editor.getCaretModel().getOffset();
+
     List<TemplateImpl> candidatesWithoutArgument = findMatchingTemplates(text, caretOffset, shortcutChar, templateSettings, false);
 
     int argumentOffset = passArgumentBack(text, caretOffset);
@@ -268,7 +309,9 @@ public class TemplateManagerImpl extends TemplateManager implements ProjectCompo
     }
     List<TemplateImpl> candidatesWithArgument = findMatchingTemplates(text, argumentOffset, shortcutChar, templateSettings, true);
 
-    if (candidatesWithArgument.isEmpty() && candidatesWithoutArgument.isEmpty()) return false;
+    if (candidatesWithArgument.isEmpty() && candidatesWithoutArgument.isEmpty()) {
+      return null;
+    }
 
     CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
       public void run() {
@@ -281,27 +324,31 @@ public class TemplateManagerImpl extends TemplateManager implements ProjectCompo
     Map<TemplateImpl, String> candidate2Argument = new HashMap<TemplateImpl, String>();
     addToMap(candidate2Argument, candidatesWithoutArgument, null);
     addToMap(candidate2Argument, candidatesWithArgument, argument);
+    return candidate2Argument;
+  }
 
-    if (candidate2Argument.isEmpty()) {
+  private boolean startNonCustomTemplates(Map<TemplateImpl, String> template2argument,
+                                          Editor editor,
+                                          PairProcessor<String, String> processor) {
+    final int caretOffset = editor.getCaretModel().getOffset();
+    final Document document = editor.getDocument();
+    CharSequence text = document.getCharsSequence();
+
+    if (template2argument == null || template2argument.size() == 0) {
       return false;
     }
     if (!FileDocumentManager.getInstance().requestWriting(editor.getDocument(), myProject)) {
       return false;
     }
 
-    if (candidate2Argument.size() == 1) {
-      TemplateImpl template = candidate2Argument.keySet().iterator().next();
-      if (candidatesWithoutArgument.size() == 1) {
-        int templateStart = caretOffset - template.getKey().length();
-        startTemplateWithPrefix(editor, template, templateStart, processor, null);
-      }
-      else {
-        int templateStart = argumentOffset - template.getKey().length();
-        startTemplateWithPrefix(editor, template, templateStart, processor, argument);
-      }
+    if (template2argument.size() == 1) {
+      TemplateImpl template = template2argument.keySet().iterator().next();
+      String argument = template2argument.get(template);
+      int templateStart = getTemplateStart(template, argument, caretOffset, text);
+      startTemplateWithPrefix(editor, template, templateStart, processor, argument);
     }
     else {
-      ListTemplatesHandler.showTemplatesLookup(myProject, editor, candidate2Argument);
+      ListTemplatesHandler.showTemplatesLookup(myProject, editor, template2argument);
     }
     return true;
   }