/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.mvnsearch.snippet.plugin.actions;
import com.intellij.codeInsight.lookup.*;
import com.intellij.openapi.actionSystem.DataConstants;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorAction;
import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaDirectoryService;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiPackage;
import org.mvnsearch.snippet.SnippetSearchAgentsFactory;
import org.mvnsearch.snippet.impl.mvnsearch.SnippetService;
import org.mvnsearch.snippet.plugin.SnippetAppComponent;
import java.util.ArrayList;
import java.util.List;
/**
* insert snippet fragment action in editor
*
* @author linux_china@hotmail.com
*/
public class InsertSnippetFragmentAction extends EditorAction {
/**
* construct snippet fragment action
*/
public InsertSnippetFragmentAction() {
super(new InsertSnippetFragmentHandler());
}
/**
* insert snippet fragment handler
*/
public static class InsertSnippetFragmentHandler extends EditorWriteActionHandler {
/**
* execute insert logic
*
* @param editor editor
* @param dataContext data context
*/
public void executeWriteAction(final Editor editor, DataContext dataContext) {
// Get position before caret
int caretOffset = editor.getCaretModel().getOffset();
int documentOffset = caretOffset - 1;
if (documentOffset > 0) {
StringBuilder prefixBuilder = new StringBuilder();
CharSequence charSequence = editor.getDocument().getCharsSequence();
for (char c; documentOffset >= 0 && isMnemonicPart(c = charSequence.charAt(documentOffset)); documentOffset--) {
prefixBuilder.append(c);
}
documentOffset = caretOffset;
StringBuilder suffixBuilder = new StringBuilder();
for (char c; documentOffset < charSequence.length() && isMnemonicPart(c = charSequence.charAt(documentOffset)); documentOffset++) {
suffixBuilder.append(c);
}
SnippetService snippetService = SnippetAppComponent.getInstance().getSnippetService();
if (snippetService != null) {
final PsiFile currentPsiFile = (PsiFile) dataContext.getData(DataConstants.PSI_FILE);
//delete snippet mnemonic and replaced by fragment content
final String prefix = prefixBuilder.reverse().toString();
String suffix = suffixBuilder.reverse().toString();
String mnemonic = prefix + suffix;
final int offset1 = caretOffset - prefix.length();
int offset2 = caretOffset;
if (!StringUtil.isEmpty(suffix)) {
offset2 = caretOffset + suffix.length();
}
final int offset3 = offset2;
boolean result = executeSnippetInsert(editor, offset1, offset2, currentPsiFile, mnemonic);
if (!result) { //snippet not found
List<String> variants = snippetService.findMnemonicListWithNameAndIcon(prefix);
List<LookupElement> lookupItems = new ArrayList<LookupElement>();
for (String variant : variants) {
String[] parts = variant.split(":", 3);
String lookupString = parts[1] + ":" + parts[2];
LookupElementBuilder lookupElement = LookupElementBuilder.create(lookupString, parts[1]);
lookupElement = lookupElement.withIcon(IconLoader.findIcon("/org/mvnsearch/snippet/plugin/icons/category/" + parts[0]));
lookupElement = lookupElement.withTypeText(parts[2]);
lookupItems.add(lookupElement);
}
LookupElement[] items = new LookupElement[lookupItems.size()];
items = lookupItems.toArray(items);
LookupManager lookupManager = LookupManager.getInstance(editor.getProject());
LookupEx lookupEx = lookupManager.showLookup(editor, items, prefix);
if (lookupEx != null) {
lookupEx.addLookupListener(new LookupAdapter() {
public void itemSelected(LookupEvent lookupEvent) {
super.itemSelected(lookupEvent);
if (lookupEvent != null && lookupEvent.getItem() != null) {
@SuppressWarnings("ConstantConditions")
String lookupString = lookupEvent.getItem().getLookupString();
executeSnippetInsert(editor, offset1, offset3 - prefix.length() + lookupString.length(), currentPsiFile, lookupString.split(":")[0]);
}
}
});
}
}
}
}
}
}
/**
* add macro support
*
* @param psiFile psi file
* @param rawCode raw code
* @return new code
*/
private static String addMacroSupport(PsiFile psiFile, String rawCode) {
String newCode = rawCode;
VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile != null) {
String fileName = virtualFile.getName();
newCode = newCode.replace("${file_name}", fileName);
if (!SnippetSearchAgentsFactory.RubyMinePlugin) {
PsiDirectory psiDirectory = psiFile.getParent();
if (psiDirectory != null) {
PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage(psiDirectory);
if (psiPackage != null && psiPackage.getName() != null) {
newCode = newCode.replace("${package}", psiPackage.getName());
}
}
}
}
return newCode;
}
/**
* validator is part of mnemonic
*
* @param part part character
* @return validator result
*/
public static boolean isMnemonicPart(char part) {
return Character.isJavaIdentifierPart(part) || part == '-';
}
/**
* execute snippet insert
*
* @param editor editor
* @param offset1 offset 1
* @param offset2 offset 2
* @param psiFile current psi file
* @param mnemonic mnemonic
* @return success mark
*/
public static boolean executeSnippetInsert(final Editor editor, final int offset1, final int offset2, PsiFile psiFile, String mnemonic) {
SnippetService snippetService = SnippetAppComponent.getInstance().getSnippetService();
String rawCode = snippetService.renderTemplate(mnemonic, null, null, SnippetAppComponent.getInstance().userName);
if (StringUtil.isNotEmpty(rawCode)) { //found and replace
//\r is forbidden for Intellij document
rawCode = rawCode.replaceAll("\r", "");
final String code = addMacroSupport(psiFile, rawCode);
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
editor.getDocument().replaceString(offset1, offset2, code);
}
});
return true;
}
return false;
}
}