Merge branch 'master' of git@git.labs.intellij.net:idea/community
authorEugene Kudelevsky <Eugene.Kudelevsky@jetbrains.com>
Wed, 10 Mar 2010 14:31:25 +0000 (17:31 +0300)
committerEugene Kudelevsky <Eugene.Kudelevsky@jetbrains.com>
Wed, 10 Mar 2010 14:31:25 +0000 (17:31 +0300)
platform/lang-impl/src/com/intellij/codeInsight/template/CustomLiveTemplate.java
platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateManagerImpl.java
xml/impl/src/com/intellij/codeInsight/template/XmlZenCodingTemplate.java

index 959aa06fa19a3aca2e4b2cb7990e96e4618ad3d5..dd4a87574b32decea7c0b3b490bfa21f75851d0a 100644 (file)
@@ -29,5 +29,9 @@ public interface CustomLiveTemplate {
   @Nullable
   String computeTemplateKey(@NotNull CustomTemplateCallback callback);
 
-  void execute(String key, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener);
+  boolean isApplicable(PsiFile file, int offset, boolean selection);
+
+  void expand(String key, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener);
+
+  void wrap(String selection, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener);
 }
index 40bcb468401be305022c14f2d88de8f88fe1869d..d8baf07f3855e2f4a225e2aebcda31c4f64bf10f 100644 (file)
@@ -243,21 +243,23 @@ public class TemplateManagerImpl extends TemplateManager implements ProjectCompo
 
     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 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;
+        int caretOffset = editor.getCaretModel().getOffset();
+        if (customLiveTemplate.isApplicable(file, caretOffset, false)) {
+          final CustomTemplateCallback callback = new CustomTemplateCallback(editor, file);
+          callback.fixInitialEditorState();
+          String key = customLiveTemplate.computeTemplateKey(callback);
+          if (key != null) {
+            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.expand(key, callback, new TemplateInvokationListener() {
+                public void finished(boolean inSeparateEvent) {
+                  callback.finish();
+                }
+              });
+              return true;
+            }
           }
         }
       }
index aba89778bda3a60f0ae820dbda0b4c5be5ab4aca..4734d0c1ccdaade00c13ee513c8b70c61a131377 100644 (file)
@@ -19,11 +19,14 @@ import com.intellij.application.options.editor.WebEditorOptions;
 import com.intellij.codeInsight.template.impl.TemplateImpl;
 import com.intellij.lang.xml.XMLLanguage;
 import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.openapi.util.Pair;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiFile;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.xml.XmlTag;
+import com.intellij.psi.xml.XmlToken;
+import com.intellij.psi.xml.XmlTokenType;
 import com.intellij.util.containers.HashMap;
 import com.intellij.util.containers.HashSet;
 import org.jetbrains.annotations.NotNull;
@@ -295,52 +298,61 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate {
   }
 
   public String computeTemplateKey(@NotNull CustomTemplateCallback callback) {
+    Editor editor = callback.getEditor();
+    int offset = callback.getOffset();
+    PsiElement element = callback.getFile().findElementAt(offset > 0 ? offset - 1 : offset);
+    int line = editor.getCaretModel().getLogicalPosition().line;
+    int lineStart = editor.getDocument().getLineStartOffset(line);
+    int parentStart;
+    do {
+      parentStart = element != null ? element.getTextRange().getStartOffset() : 0;
+      int startOffset = parentStart > lineStart ? parentStart : lineStart;
+      String key = computeKey(editor, startOffset);
+      List<MyToken> tokens = parse(key, callback);
+      if (tokens != null && check(tokens)) {
+        if (tokens.size() == 2) {
+          MyToken token = tokens.get(0);
+          if (token instanceof MyTemplateToken) {
+            if (key.equals(((MyTemplateToken)token).myKey) && callback.isLiveTemplateApplicable(key)) {
+              // do not activate only live template
+              return null;
+            }
+          }
+        }
+        return key;
+      }
+      if (element != null) {
+        element = element.getParent();
+      }
+    }
+    while (element != null && parentStart > lineStart);
+    return null;
+  }
+
+  public boolean isApplicable(PsiFile file, int offset, boolean selection) {
     WebEditorOptions webEditorOptions = WebEditorOptions.getInstance();
     if (!webEditorOptions.isZenCodingEnabled()) {
-      return null;
+      return false;
     }
-    PsiFile file = callback.getFile();
     if (file.getLanguage() instanceof XMLLanguage) {
-      Editor editor = callback.getEditor();
-      PsiElement element = file.findElementAt(editor.getCaretModel().getOffset() - 1);
+      PsiElement element = file.findElementAt(offset > 0 ? offset - 1 : offset);
       if (element == null || element.getLanguage() instanceof XMLLanguage) {
-        int line = editor.getCaretModel().getLogicalPosition().line;
-        int lineStart = editor.getDocument().getLineStartOffset(line);
-        int parentStart;
-        do {
-          parentStart = element != null ? element.getTextRange().getStartOffset() : 0;
-          int startOffset = parentStart > lineStart ? parentStart : lineStart;
-          String key = computeKey(editor, startOffset);
-          List<MyToken> tokens = parse(key, callback);
-          if (tokens != null && check(tokens)) {
-            if (tokens.size() == 2) {
-              MyToken token = tokens.get(0);
-              if (token instanceof MyTemplateToken) {
-                if (key.equals(((MyTemplateToken)token).myKey) && callback.isLiveTemplateApplicable(key)) {
-                  // do not activate only live template
-                  return null;
-                }
-              }
-            }
-            return key;
-          }
-          if (element != null) {
-            element = element.getParent();
-          }
-        }
-        while (element != null && parentStart > lineStart);
+        return true;
       }
     }
-    return null;
+    return false;
   }
 
-  public void execute(String key, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) {
+  public void expand(String key, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) {
     List<MyToken> tokens = parse(key, callback);
     assert tokens != null;
     MyInterpreter interpreter = new MyInterpreter(tokens, callback, MyState.WORD, listener);
     interpreter.invoke(0);
   }
 
+  public void wrap(String selection, @NotNull CustomTemplateCallback callback, @Nullable TemplateInvokationListener listener) {
+  }
+
   private static void fail() {
     LOG.error("Input string was checked incorrectly during isApplicable() invokation");
   }
@@ -440,39 +452,6 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate {
     return false;
   }*/
 
-  @Nullable
-  private static CharSequence getPrecedingTagName(CharSequence text, int index, int leftBound) {
-    int j = index - 1;
-    while (j >= leftBound && Character.isWhitespace(text.charAt(j))) {
-      j--;
-    }
-    if (j < leftBound || text.charAt(j) != '>') {
-      return null;
-    }
-    while (j >= leftBound && text.charAt(j) != '<') {
-      j--;
-    }
-    if (j < 0) {
-      return null;
-    }
-    return parseTagName(text, j + 1, index);
-  }
-
-  @Nullable
-  private static CharSequence parseTagName(CharSequence text, int index, int rightBound) {
-    int j = index;
-    if (rightBound > text.length()) {
-      rightBound = text.length();
-    }
-    while (j < rightBound && !Character.isWhitespace(text.charAt(j)) && text.charAt(j) != '>') {
-      j++;
-    }
-    if (j >= text.length()) {
-      return null;
-    }
-    return text.subSequence(index, j);
-  }
-
   private class MyInterpreter {
     private final List<MyToken> myTokens;
     private final CustomTemplateCallback myCallback;
@@ -501,22 +480,43 @@ public class XmlZenCodingTemplate implements CustomLiveTemplate {
       int endOfTemplate = myCallback.getEndOfTemplate(templateBoundsKey);
       Editor editor = myCallback.getEditor();
       int offset = myCallback.getOffset();
-      Document document = myCallback.getEditor().getDocument();
-      CharSequence text = document.getCharsSequence();
-      CharSequence tagName = getPrecedingTagName(text, offset, startOfTemplate);
+
+      PsiFile file = myCallback.getFile();
+
+      PsiElement element = file.findElementAt(offset);
+      if (element instanceof XmlToken && ((XmlToken)element).getTokenType() == XmlTokenType.XML_END_TAG_START) {
+        return;
+      }
+
+      int newOffset = -1;
+      XmlTag tag = PsiTreeUtil.findElementOfClassAtRange(file, startOfTemplate, endOfTemplate, XmlTag.class);
+      if (tag != null) {
+        for (PsiElement child : tag.getChildren()) {
+          if (child instanceof XmlToken && ((XmlToken)child).getTokenType() == XmlTokenType.XML_END_TAG_START) {
+            newOffset = child.getTextOffset();
+          }
+        }
+      }
+
+      if (newOffset >= 0) {
+        myCallback.fixEndOffset();
+        editor.getCaretModel().moveToOffset(newOffset);
+      }
+
+      /*CharSequence tagName = getPrecedingTagName(text, offset, startOfTemplate);
       if (tagName != null) {
-        /*if (!hasClosingTag(text, tagName, offset, endOfTemplate)) {
+        *//*if (!hasClosingTag(text, tagName, offset, endOfTemplate)) {
           document.insertString(offset, "</" + tagName + '>');
-        }*/
+        }*//*
       }
       else if (offset != endOfTemplate) {
         tagName = getPrecedingTagName(text, endOfTemplate, startOfTemplate);
         if (tagName != null) {
-          /*fixEndOffset();
-          document.insertString(endOfTemplate, "</" + tagName + '>');*/
+          *//*fixEndOffset();
+          document.insertString(endOfTemplate, "</" + tagName + '>');*//*
           editor.getCaretModel().moveToOffset(endOfTemplate);
         }
-      }
+      }*/
     }
 
     public boolean invoke(int startIndex) {