Package com.dmarcotte.handlebars.editor.actions

Source Code of com.dmarcotte.handlebars.editor.actions.HbTypedHandler

package com.dmarcotte.handlebars.editor.actions;

import com.dmarcotte.handlebars.HbLanguage;
import com.dmarcotte.handlebars.config.HbConfig;
import com.dmarcotte.handlebars.file.HbFileViewProvider;
import com.dmarcotte.handlebars.parsing.HbTokenTypes;
import com.dmarcotte.handlebars.psi.*;
import com.intellij.codeInsight.editorActions.TypedHandlerDelegate;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.CaretModel;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;

/**
* Handler for custom plugin actions on chars typed by the user.  See {@link HbEnterHandler} for custom actions
* on Enter.
*/
public class HbTypedHandler extends TypedHandlerDelegate {

  @Override
  public Result beforeCharTyped(char c, Project project, Editor editor, PsiFile file, FileType fileType) {
    int offset = editor.getCaretModel().getOffset();

    if (offset == 0 || offset > editor.getDocument().getTextLength()) {
      return Result.CONTINUE;
    }

    String previousChar = editor.getDocument().getText(new TextRange(offset - 1, offset));

    if (file.getViewProvider() instanceof HbFileViewProvider) {
      PsiDocumentManager.getInstance(project).commitAllDocuments();

      // we suppress the built-in "}" auto-complete when we see "{{"
      if (c == '{' && previousChar.equals("{")) {
        // since the "}" autocomplete is built in to IDEA, we need to hack around it a bit by
        // intercepting it before it is inserted, doing the work of inserting for the user
        // by inserting the '{' the user just typed...
        editor.getDocument().insertString(offset, Character.toString(c));
        // ... and position their caret after it as they'd expect...
        editor.getCaretModel().moveToOffset(offset + 1);

        // ... then finally telling subsequent responses to this charTyped to do nothing
        return Result.STOP;
      }
    }

    return Result.CONTINUE;
  }

  @Override
  public Result charTyped(char c, Project project, Editor editor, @NotNull PsiFile file) {
    int offset = editor.getCaretModel().getOffset();
    FileViewProvider provider = file.getViewProvider();

    if (offset < 2 || offset > editor.getDocument().getTextLength()) {
      return Result.CONTINUE;
    }

    String previousChar = editor.getDocument().getText(new TextRange(offset - 2, offset - 1));
    boolean closeBraceCompleted = false;

    if (provider instanceof HbFileViewProvider) {
      if (HbConfig.isAutocompleteMustachesEnabled() && c == '}' && !previousChar.equals("}")) {
        // we may be able to complete the second brace
        PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
        PsiElement elementAt = provider.findElementAt(offset - 1, HbLanguage.class);
        ASTNode node = elementAt != null ? elementAt.getNode() : null;
        if (node != null && node.getElementType() == HbTokenTypes.INVALID) {
          // we should be looking at the beginning of a close brace.  Find its matching open brace and auto-complete based on its type
          PsiElement mustache = PsiTreeUtil.findFirstParent(elementAt, new Condition<PsiElement>() {
            @Override
            public boolean value(PsiElement psiElement) {
              return psiElement instanceof HbMustache;
            }
          });

          if (mustache != null) {
            String braceCompleter;

            if (mustache.getFirstChild().getNode().getElementType() == HbTokenTypes.OPEN_UNESCAPED) {
              // add "}}" to complete the CLOSE_UNESCAPED
              braceCompleter = "}}";
            } else {
              // add "}" to complete the CLOSE
              braceCompleter = "}";
            }

            editor.getDocument().insertString(offset, braceCompleter);
            offset = offset + braceCompleter.length();
            editor.getCaretModel().moveToOffset(offset);
            closeBraceCompleted = true;
          }
        }
      }
    }

    // if we just completed a close brace or the user just typed one, we may have some business to attend to
    if (closeBraceCompleted || (c == '}' && previousChar.equals("}"))) {
      autoInsertCloseTag(project, offset, editor, provider);
      adjustMustacheFormatting(project, offset, editor, file, provider);
    }

    return Result.CONTINUE;
  }

  /**
   * When appropriate, auto-inserts Handlebars close tags.  i.e.  When "{{#tagId}}" or "{{^tagId}} is typed,
   * {{/tagId}} is automatically inserted
   */
  private static void autoInsertCloseTag(Project project, int offset, Editor editor, FileViewProvider provider) {
    if (!HbConfig.isAutoGenerateCloseTagEnabled()) {
      return;
    }

    PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());

    PsiElement elementAtCaret = provider.findElementAt(offset - 1, HbLanguage.class);

    if (elementAtCaret == null || elementAtCaret.getNode().getElementType() != HbTokenTypes.CLOSE) {
      return;
    }

    HbOpenBlockMustache openTag = HbPsiUtil.findParentOpenTagElement(elementAtCaret);

    if (openTag != null && openTag.getChildren().length > 1) {
      HbMustacheName mustacheName = PsiTreeUtil.findChildOfType(openTag, HbMustacheName.class);

      if (mustacheName != null) {
        // insert the corresponding close tag
        editor.getDocument().insertString(offset, "{{/" + mustacheName.getText() + "}}");
      }
    }
  }

  /**
   * When appropriate, adjusts the formatting for some 'staches, particularily close 'staches
   * and simple inverses ("{{^}}" and "{{else}}")
   */
  private static void adjustMustacheFormatting(Project project, int offset, Editor editor, PsiFile file, FileViewProvider provider) {
    if (!HbConfig.isFormattingEnabled()) {
      // formatting disabled; nothing to do
      return;
    }

    PsiElement elementAtCaret = provider.findElementAt(offset - 1, HbLanguage.class);
    PsiElement closeOrSimpleInverseParent = PsiTreeUtil.findFirstParent(elementAtCaret, true, new Condition<PsiElement>() {
      @Override
      public boolean value(PsiElement element) {
        return element != null
               && (element instanceof HbSimpleInverse
                   || element instanceof HbCloseBlockMustache);
      }
    });

    // run the formatter if the user just completed typing a SIMPLE_INVERSE or a CLOSE_BLOCK_STACHE
    if (closeOrSimpleInverseParent != null) {
      // grab the current caret position (AutoIndentLinesHandler is about to mess with it)
      PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
      CaretModel caretModel = editor.getCaretModel();
      CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
      codeStyleManager.adjustLineIndent(file, editor.getDocument().getLineStartOffset(caretModel.getLogicalPosition().line));
    }
  }
}
TOP

Related Classes of com.dmarcotte.handlebars.editor.actions.HbTypedHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.