mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Extract Java live postfix templates provider
This commit is contained in:
+3
-18
@@ -21,34 +21,19 @@ import com.intellij.codeInsight.template.CustomLiveTemplate;
|
||||
import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
|
||||
import com.intellij.codeInsight.template.postfix.templates.PostfixLiveTemplate;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.patterns.ElementPattern;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.patterns.PlatformPatterns;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.impl.source.tree.ElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.patterns.PsiJavaPatterns.psiElement;
|
||||
import static com.intellij.patterns.StandardPatterns.string;
|
||||
|
||||
public class PostfixTemplateCompletionContributor extends CompletionContributor {
|
||||
private static final TokenSet SUITABLE_ELEMENTS = TokenSet.orSet(ElementType.KEYWORD_BIT_SET,
|
||||
ElementType.LITERAL_BIT_SET,
|
||||
TokenSet.create(JavaTokenType.IDENTIFIER));
|
||||
|
||||
public PostfixTemplateCompletionContributor() {
|
||||
extend(CompletionType.BASIC, identifierAfterDot(), new PostfixTemplatesCompletionProvider());
|
||||
extend(CompletionType.BASIC, PlatformPatterns.psiElement(), new PostfixTemplatesCompletionProvider());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PostfixLiveTemplate getPostfixLiveTemplate(@NotNull PsiFile file, @NotNull Editor editor) {
|
||||
public static PostfixLiveTemplate getPostfixLiveTemplate(@NotNull PsiFile file, @NotNull Editor editor) {
|
||||
PostfixLiveTemplate postfixLiveTemplate = CustomLiveTemplate.EP_NAME.findExtension(PostfixLiveTemplate.class);
|
||||
return postfixLiveTemplate != null && TemplateManagerImpl.isApplicable(postfixLiveTemplate, editor, file) ? postfixLiveTemplate : null;
|
||||
}
|
||||
|
||||
private static ElementPattern<? extends PsiElement> identifierAfterDot() {
|
||||
return psiElement().withElementType(SUITABLE_ELEMENTS).afterLeaf(psiElement().withText(string().contains(".")));
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -42,11 +42,12 @@ class PostfixTemplatesCompletionProvider extends CompletionProvider<CompletionPa
|
||||
PostfixLiveTemplate postfixLiveTemplate = getPostfixLiveTemplate(parameters.getOriginalFile(), parameters.getEditor());
|
||||
if (postfixLiveTemplate != null) {
|
||||
postfixLiveTemplate.addCompletions(parameters, result.withPrefixMatcher(new MyPrefixMatcher(result.getPrefixMatcher().getPrefix())));
|
||||
CharSequence documentContent = parameters.getEditor().getDocument().getCharsSequence();
|
||||
String possibleKey = postfixLiveTemplate.computeTemplateKeyWithoutContextChecking(documentContent, parameters.getOffset());
|
||||
String possibleKey = postfixLiveTemplate
|
||||
.computeTemplateKeyWithoutContextChecking(parameters.getOriginalFile(), parameters.getEditor(), parameters.getOffset());
|
||||
if (possibleKey != null) {
|
||||
result = result.withPrefixMatcher(possibleKey);
|
||||
result.restartCompletionOnPrefixChange(StandardPatterns.string().oneOf(postfixLiveTemplate.getAllTemplateKeys()));
|
||||
result.restartCompletionOnPrefixChange(
|
||||
StandardPatterns.string().oneOf(postfixLiveTemplate.getAllTemplateKeys(parameters.getOriginalFile(), parameters.getOffset())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-3
@@ -19,7 +19,11 @@ import com.intellij.application.options.editor.EditorOptionsProvider;
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.template.impl.LiveTemplateCompletionContributor;
|
||||
import com.intellij.codeInsight.template.impl.TemplateSettings;
|
||||
import com.intellij.codeInsight.template.postfix.templates.LanguagePostfixTemplate;
|
||||
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate;
|
||||
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateProvider;
|
||||
import com.intellij.lang.LanguageExtensionPoint;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.SearchableConfigurable;
|
||||
@@ -34,8 +38,9 @@ import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PostfixTemplatesConfigurable implements SearchableConfigurable, EditorOptionsProvider, Configurable.NoScroll {
|
||||
@Nullable
|
||||
@@ -61,7 +66,14 @@ public class PostfixTemplatesConfigurable implements SearchableConfigurable, Edi
|
||||
}
|
||||
|
||||
myTemplatesSettings = settings;
|
||||
List<PostfixTemplate> templates = Arrays.asList(PostfixTemplate.EP_NAME.getExtensions());
|
||||
|
||||
LanguageExtensionPoint[] extensions = new ExtensionPointName<LanguageExtensionPoint>(LanguagePostfixTemplate.EP_NAME).getExtensions();
|
||||
|
||||
List<PostfixTemplate> templates = ContainerUtil.newArrayList();
|
||||
for (LanguageExtensionPoint extension : extensions) {
|
||||
templates.addAll(((PostfixTemplateProvider)extension.getInstance()).getTemplates());
|
||||
}
|
||||
|
||||
ContainerUtil.sort(templates, new Comparator<PostfixTemplate>() {
|
||||
@Override
|
||||
public int compare(PostfixTemplate o1, PostfixTemplate o2) {
|
||||
@@ -184,7 +196,7 @@ public class PostfixTemplatesConfigurable implements SearchableConfigurable, Edi
|
||||
private static String shortcutToString(char shortcut) {
|
||||
if (shortcut == TemplateSettings.SPACE_CHAR) {
|
||||
return SPACE;
|
||||
}
|
||||
}
|
||||
if (shortcut == TemplateSettings.ENTER_CHAR) {
|
||||
return ENTER;
|
||||
}
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.intellij.codeInsight.template.postfix.templates;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionInitializationContext;
|
||||
import com.intellij.codeInsight.completion.JavaCompletionContributor;
|
||||
import com.intellij.codeInsight.template.CustomTemplateCallback;
|
||||
import com.intellij.codeInsight.template.postfix.util.Aliases;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
public class JavaPostfixTemplateProvider implements PostfixTemplateProvider {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(JavaPostfixTemplateProvider.class);
|
||||
|
||||
private final Map<String, PostfixTemplate> myMapTemplates;
|
||||
|
||||
public JavaPostfixTemplateProvider() {
|
||||
myMapTemplates = ContainerUtil.newHashMap();
|
||||
for (PostfixTemplate template : getInitializeTemplateSet()) {
|
||||
register(template.getKey(), template);
|
||||
Aliases aliases = template.getClass().getAnnotation(Aliases.class);
|
||||
if (aliases != null) {
|
||||
for (String key : aliases.value()) {
|
||||
register(key, template);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Set<PostfixTemplate> getInitializeTemplateSet() {
|
||||
return ContainerUtil.newHashSet(new AssertStatementPostfixTemplate(),
|
||||
new CastExpressionPostfixTemplate(),
|
||||
new ElseStatementPostfixTemplate(),
|
||||
new ForAscendingPostfixTemplate(),
|
||||
new ForDescendingPostfixTemplate(),
|
||||
new ForeachPostfixTemplate(),
|
||||
new FormatPostfixTemplate(),
|
||||
new IfStatementPostfixTemplate(),
|
||||
new InstanceofExpressionPostfixTemplate(),
|
||||
new IntroduceFieldPostfixTemplate(),
|
||||
new IntroduceVariablePostfixTemplate(),
|
||||
new IsNullCheckPostfixTemplate(),
|
||||
new NotExpressionPostfixTemplate(),
|
||||
new NotNullCheckPostfixTemplate(),
|
||||
new ParenthesizedExpressionPostfixTemplate(),
|
||||
new ReturnStatementPostfixTemplate(),
|
||||
new SoutPostfixTemplate(),
|
||||
new SwitchStatementPostfixTemplate(),
|
||||
new SynchronizedStatementPostfixTemplate(),
|
||||
new ThrowExceptionPostfixTemplate(),
|
||||
new TryStatementPostfixTemplate(),
|
||||
new TryWithResourcesPostfixTemplate(),
|
||||
new WhileStatementPostfixTemplate());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<PostfixTemplate> getTemplates() {
|
||||
return ContainerUtil.newHashSet(myMapTemplates.values());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> getKeys() {
|
||||
return myMapTemplates.keySet();
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PostfixTemplate get(@Nullable String key) {
|
||||
return myMapTemplates.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminalSymbol(char currentChar) {
|
||||
return currentChar == '.' || currentChar == '!';
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElement preExpand(@NotNull Editor editor, @NotNull PsiElement context, int offset, @NotNull final String key) {
|
||||
|
||||
return addSemicolonIfNeeded(editor, editor.getDocument(), context, offset - key.length());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiFile preCheck(@NotNull Editor editor, @NotNull PsiFile file, int currentOffset) {
|
||||
Document document = file.getViewProvider().getDocument();
|
||||
assert document != null;
|
||||
CharSequence sequence = document.getCharsSequence();
|
||||
StringBuilder fileContentWithoutKey = new StringBuilder(sequence);
|
||||
if (isSemicolonNeeded(file, editor)) {
|
||||
fileContentWithoutKey.insert(currentOffset, ';');
|
||||
file = PostfixLiveTemplate.copyFile(file, fileContentWithoutKey);
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
private void register(@NotNull String key, @NotNull PostfixTemplate template) {
|
||||
PostfixTemplate registered = myMapTemplates.put(key, template);
|
||||
if (registered != null) {
|
||||
LOG.error("Can't register postfix template. Duplicated key: " + template.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiElement addSemicolonIfNeeded(@NotNull final Editor editor,
|
||||
@NotNull final Document document,
|
||||
@NotNull final PsiElement context,
|
||||
final int offset) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
|
||||
final Ref<PsiElement> newContext = Ref.create(context);
|
||||
final PsiFile file = context.getContainingFile();
|
||||
if (isSemicolonNeeded(file, editor)) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
|
||||
public void run() {
|
||||
document.insertString(offset, ";");
|
||||
PsiDocumentManager.getInstance(context.getProject()).commitDocument(document);
|
||||
newContext.set(CustomTemplateCallback.getContext(file, offset - 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return newContext.get();
|
||||
}
|
||||
|
||||
private static boolean isSemicolonNeeded(@NotNull PsiFile file, @NotNull Editor editor) {
|
||||
return JavaCompletionContributor.semicolonNeeded(editor, file, CompletionInitializationContext.calcStartOffset(editor));
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.intellij.codeInsight.template.postfix.templates;
|
||||
|
||||
import com.intellij.lang.LanguageExtension;
|
||||
|
||||
public class LanguagePostfixTemplate extends LanguageExtension<PostfixTemplateProvider> {
|
||||
public static final LanguagePostfixTemplate INSTANCE = new LanguagePostfixTemplate();
|
||||
public static final String EP_NAME = "com.intellij.codeInsight.template.postfixTemplateProvider";
|
||||
|
||||
private LanguagePostfixTemplate() {
|
||||
super(EP_NAME);
|
||||
}
|
||||
}
|
||||
+175
-148
@@ -15,17 +15,15 @@
|
||||
*/
|
||||
package com.intellij.codeInsight.template.postfix.templates;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionInitializationContext;
|
||||
import com.intellij.codeInsight.completion.JavaCompletionContributor;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.codeInsight.template.CustomLiveTemplateBase;
|
||||
import com.intellij.codeInsight.template.CustomTemplateCallback;
|
||||
import com.intellij.codeInsight.template.impl.CustomLiveTemplateLookupElement;
|
||||
import com.intellij.codeInsight.template.impl.TemplateSettings;
|
||||
import com.intellij.codeInsight.template.postfix.completion.PostfixTemplateLookupElement;
|
||||
import com.intellij.codeInsight.template.postfix.settings.PostfixTemplatesSettings;
|
||||
import com.intellij.codeInsight.template.postfix.util.Aliases;
|
||||
import com.intellij.featureStatistics.FeatureUsageTracker;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.command.undo.UndoConstants;
|
||||
@@ -33,7 +31,6 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
@@ -42,71 +39,53 @@ import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiFileFactory;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public class PostfixLiveTemplate extends CustomLiveTemplateBase {
|
||||
public static final String POSTFIX_TEMPLATE_ID = "POSTFIX_TEMPLATE_ID";
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(PostfixLiveTemplate.class);
|
||||
private final HashMap<String, PostfixTemplate> myTemplates = ContainerUtil.newHashMap();
|
||||
private static final LanguagePostfixTemplate templates = LanguagePostfixTemplate.INSTANCE;
|
||||
|
||||
public PostfixLiveTemplate() {
|
||||
for (PostfixTemplate template : PostfixTemplate.EP_NAME.getExtensions()) {
|
||||
register(template.getKey(), template);
|
||||
Aliases aliases = template.getClass().getAnnotation(Aliases.class);
|
||||
if (aliases != null) {
|
||||
for (String key : aliases.value()) {
|
||||
register(key, template);
|
||||
}
|
||||
@NotNull
|
||||
public Set<String> getAllTemplateKeys(PsiFile file, int offset) {
|
||||
Set<String> keys = Sets.newHashSet();
|
||||
Language language = PsiUtilCore.getLanguageAtOffset(file, offset);
|
||||
|
||||
for (PostfixTemplateProvider provider : templates.allForLanguage(language)) {
|
||||
keys.addAll(provider.getKeys());
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
public boolean hasNotEmptyKey(PsiFile file, int offset) {
|
||||
Language language = PsiUtilCore.getLanguageAtOffset(file, offset);
|
||||
for (PostfixTemplateProvider provider : templates.allForLanguage(language)) {
|
||||
if (StringUtil
|
||||
.isNotEmpty(computeTemplateKeyWithoutContextChecking(provider, file.getText(), offset + 1))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void register(@NotNull String key, @NotNull PostfixTemplate template) {
|
||||
PostfixTemplate registered = myTemplates.put(key, template);
|
||||
if (registered != null) {
|
||||
LOG.error("Can't register postfix template. Duplicated key: " + template.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String computeTemplateKey(@NotNull CustomTemplateCallback callback) {
|
||||
Editor editor = callback.getEditor();
|
||||
String key = computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), editor.getCaretModel().getOffset());
|
||||
if (key == null) return null;
|
||||
return isApplicableTemplate(getTemplateByKey(key), key, callback.getContext().getContainingFile(), editor) ? key : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String computeTemplateKeyWithoutContextChecking(@NotNull CustomTemplateCallback callback) {
|
||||
Editor editor = callback.getEditor();
|
||||
return computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), editor.getCaretModel().getOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMultiCaret() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String computeTemplateKeyWithoutContextChecking(@NotNull CharSequence documentContent, int currentOffset) {
|
||||
public String computeTemplateKeyWithoutContextChecking(PostfixTemplateProvider provider,
|
||||
@NotNull CharSequence documentContent,
|
||||
int currentOffset) {
|
||||
int startOffset = currentOffset;
|
||||
if (documentContent.length() < startOffset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (startOffset > 0) {
|
||||
char currentChar = documentContent.charAt(startOffset - 1);
|
||||
if (!Character.isJavaIdentifierPart(currentChar)) {
|
||||
if (currentChar != '.' && currentChar != '!') {
|
||||
if (!provider.isTerminalSymbol(currentChar)) {
|
||||
return null;
|
||||
}
|
||||
startOffset--;
|
||||
@@ -117,19 +96,82 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase {
|
||||
return String.valueOf(documentContent.subSequence(startOffset, currentOffset));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String computeTemplateKey(@NotNull CustomTemplateCallback callback) {
|
||||
Editor editor = callback.getEditor();
|
||||
CharSequence charsSequence = editor.getDocument().getCharsSequence();
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
Language language = getLanguage(callback);
|
||||
for (PostfixTemplateProvider provider : templates.allForLanguage(language)) {
|
||||
String key = computeTemplateKeyWithoutContextChecking(provider, charsSequence, offset);
|
||||
if (key != null && isApplicableTemplate(provider, key, callback.getFile(), editor)) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String computeTemplateKeyWithoutContextChecking(@NotNull CustomTemplateCallback callback) {
|
||||
Editor editor = callback.getEditor();
|
||||
return computeTemplateKeyWithoutContextChecking(callback.getFile(), editor, editor.getCaretModel().getOffset());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMultiCaret() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String computeTemplateKeyWithoutContextChecking(PsiFile file, Editor editor, int currentOffset) {
|
||||
Language language = PsiUtilCore.getLanguageAtOffset(file, currentOffset);
|
||||
for (PostfixTemplateProvider provider : templates.allForLanguage(language)) {
|
||||
String key = computeTemplateKeyWithoutContextChecking(provider, editor.getDocument().getCharsSequence(), currentOffset);
|
||||
if (key != null) return key;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expand(@NotNull final String key, @NotNull final CustomTemplateCallback callback) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
|
||||
FeatureUsageTracker.getInstance().triggerFeatureUsed("editing.completion.postfix");
|
||||
|
||||
final PostfixTemplate template = getTemplateByKey(key);
|
||||
Editor editor = callback.getEditor();
|
||||
Language language = getLanguage(callback);
|
||||
for (PostfixTemplateProvider provider : templates.allForLanguage(language)) {
|
||||
PostfixTemplate postfixTemplate = provider.get(key);
|
||||
if (postfixTemplate != null) {
|
||||
expandForProvider(provider, key, callback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// don't care about errors in multiCaret mode
|
||||
if (editor.getCaretModel().getAllCarets().size() == 1) {
|
||||
LOG.error("Template not found by key: " + key);
|
||||
}
|
||||
}
|
||||
|
||||
private static Language getLanguage(CustomTemplateCallback callback) {
|
||||
return PsiUtilCore.getLanguageAtOffset(callback.getFile(), callback.getEditor().getCaretModel().getOffset());
|
||||
}
|
||||
|
||||
public void expandForProvider(
|
||||
@NotNull PostfixTemplateProvider provider,
|
||||
@NotNull final String key,
|
||||
@NotNull final CustomTemplateCallback callback) {
|
||||
final Editor editor = callback.getEditor();
|
||||
final PsiFile file = callback.getContext().getContainingFile();
|
||||
if (isApplicableTemplate(template, key, file, editor)) {
|
||||
if (isApplicableTemplate(provider, key, file, editor)) {
|
||||
int currentOffset = editor.getCaretModel().getOffset();
|
||||
PsiElement newContext = deleteTemplateKey(file, editor.getDocument(), currentOffset, key);
|
||||
newContext = addSemicolonIfNeeded(editor, editor.getDocument(), newContext, currentOffset - key.length());
|
||||
newContext = provider.preExpand(editor, newContext, currentOffset, key);
|
||||
PostfixTemplate template = provider.get(key);
|
||||
assert template != null;
|
||||
expandTemplate(template, editor, newContext);
|
||||
}
|
||||
// don't care about errors in multiCaret mode
|
||||
@@ -141,11 +183,10 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase {
|
||||
@Override
|
||||
public boolean isApplicable(PsiFile file, int offset, boolean wrapping) {
|
||||
PostfixTemplatesSettings settings = PostfixTemplatesSettings.getInstance();
|
||||
if (wrapping || file == null || settings == null || !settings.isPostfixTemplatesEnabled() ||
|
||||
PsiUtilCore.getLanguageAtOffset(file, offset) != JavaLanguage.INSTANCE) {
|
||||
if (wrapping || file == null || settings == null || !settings.isPostfixTemplatesEnabled()) {
|
||||
return false;
|
||||
}
|
||||
return StringUtil.isNotEmpty(computeTemplateKeyWithoutContextChecking(file.getText(), offset + 1));
|
||||
return hasNotEmptyKey(file, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,35 +218,46 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<? extends CustomLiveTemplateLookupElement> getLookupElements(@NotNull PsiFile file, @NotNull Editor editor, int offset) {
|
||||
String key = computeTemplateKeyWithoutContextChecking(editor.getDocument().getCharsSequence(), offset);
|
||||
public Collection<? extends CustomLiveTemplateLookupElement> getLookupElements(@NotNull PsiFile file,
|
||||
@NotNull Editor editor,
|
||||
int offset) {
|
||||
Collection<CustomLiveTemplateLookupElement> result = ContainerUtil.newHashSet();
|
||||
Language language = PsiUtilCore.getLanguageAtOffset(file, editor.getCaretModel().getOffset());
|
||||
for (PostfixTemplateProvider provider : templates.allForLanguage(language)) {
|
||||
result.addAll(getLookupElementsForProvider(provider, file, editor, offset));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Collection<? extends CustomLiveTemplateLookupElement> getLookupElementsForProvider(
|
||||
@NotNull PostfixTemplateProvider provider,
|
||||
@NotNull PsiFile file,
|
||||
@NotNull Editor editor,
|
||||
int offset) {
|
||||
String key = computeTemplateKeyWithoutContextChecking(file, editor, offset);
|
||||
if (key != null && editor.getCaretModel().getCaretCount() == 1) {
|
||||
Collection<CustomLiveTemplateLookupElement> result = ContainerUtil.newHashSet();
|
||||
Condition<PostfixTemplate> isApplicationTemplateFunction = createIsApplicationTemplateFunction(key, file, editor);
|
||||
for (Map.Entry<String, PostfixTemplate> entry : myTemplates.entrySet()) {
|
||||
PostfixTemplate postfixTemplate = entry.getValue();
|
||||
|
||||
Condition<PostfixTemplate> isApplicationTemplateFunction = createIsApplicationTemplateFunction(provider, key, file, editor);
|
||||
for (String postfixKey : provider.getKeys()) {
|
||||
PostfixTemplate postfixTemplate = provider.get(postfixKey);
|
||||
assert postfixTemplate != null;
|
||||
if (isApplicationTemplateFunction.value(postfixTemplate)) {
|
||||
result.add(new PostfixTemplateLookupElement(this, postfixTemplate, entry.getKey(), false));
|
||||
result.add(new PostfixTemplateLookupElement(this, postfixTemplate, postfixKey, false));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return super.getLookupElements(file, editor, offset);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<String> getAllTemplateKeys() {
|
||||
return myTemplates.keySet();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PostfixTemplate getTemplateByKey(@NotNull String key) {
|
||||
return myTemplates.get(key);
|
||||
}
|
||||
|
||||
private static void expandTemplate(@NotNull final PostfixTemplate template,
|
||||
@NotNull final Editor editor,
|
||||
@NotNull final PsiElement context) {
|
||||
private static void expandTemplate(
|
||||
@NotNull final PostfixTemplate template,
|
||||
@NotNull final Editor editor,
|
||||
@NotNull final PsiElement context) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -218,56 +270,6 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase {
|
||||
});
|
||||
}
|
||||
|
||||
@Contract("null, _, _, _ -> false")
|
||||
private static boolean isApplicableTemplate(@Nullable PostfixTemplate template, @NotNull String key, @NotNull PsiFile file, @NotNull Editor editor) {
|
||||
return createIsApplicationTemplateFunction(key, file, editor).value(template);
|
||||
}
|
||||
|
||||
private static Condition<PostfixTemplate> createIsApplicationTemplateFunction(@NotNull String key, @NotNull PsiFile file, @NotNull Editor editor) {
|
||||
int currentOffset = editor.getCaretModel().getOffset();
|
||||
final int newOffset = currentOffset - key.length();
|
||||
CharSequence fileContent = editor.getDocument().getCharsSequence();
|
||||
|
||||
StringBuilder fileContentWithoutKey = new StringBuilder();
|
||||
fileContentWithoutKey.append(fileContent.subSequence(0, newOffset));
|
||||
fileContentWithoutKey.append(fileContent.subSequence(currentOffset, fileContent.length()));
|
||||
PsiFile copyFile = copyFile(file, fileContentWithoutKey);
|
||||
Document copyDocument = copyFile.getViewProvider().getDocument();
|
||||
if (copyDocument == null) {
|
||||
//noinspection unchecked
|
||||
return Condition.FALSE;
|
||||
}
|
||||
|
||||
if (isSemicolonNeeded(copyFile, editor)) {
|
||||
fileContentWithoutKey.insert(newOffset, ';');
|
||||
copyFile = copyFile(file, fileContentWithoutKey);
|
||||
copyDocument = copyFile.getViewProvider().getDocument();
|
||||
if (copyDocument == null) {
|
||||
//noinspection unchecked
|
||||
return Condition.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiElement context = CustomTemplateCallback.getContext(copyFile, newOffset > 0 ? newOffset - 1 : newOffset);
|
||||
final Document finalCopyDocument = copyDocument;
|
||||
return new Condition<PostfixTemplate>() {
|
||||
@Override
|
||||
public boolean value(PostfixTemplate template) {
|
||||
return template != null && template.isEnabled() && template.isApplicable(context, finalCopyDocument, newOffset);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiFile copyFile(@NotNull PsiFile file, @NotNull StringBuilder fileContentWithoutKey) {
|
||||
final PsiFileFactory psiFileFactory = PsiFileFactory.getInstance(file.getProject());
|
||||
PsiFile copy = psiFileFactory.createFileFromText(file.getName(), file.getFileType(), fileContentWithoutKey);
|
||||
VirtualFile vFile = copy.getVirtualFile();
|
||||
if (vFile != null) {
|
||||
vFile.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiElement deleteTemplateKey(@NotNull final PsiFile file,
|
||||
@@ -291,33 +293,58 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase {
|
||||
return CustomTemplateCallback.getContext(file, startOffset > 0 ? startOffset - 1 : startOffset);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiElement addSemicolonIfNeeded(@NotNull final Editor editor,
|
||||
@NotNull final Document document,
|
||||
@NotNull final PsiElement context,
|
||||
final int offset) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
|
||||
final Ref<PsiElement> newContext = Ref.create(context);
|
||||
final PsiFile file = context.getContainingFile();
|
||||
if (isSemicolonNeeded(file, editor)) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
|
||||
public void run() {
|
||||
document.insertString(offset, ";");
|
||||
PsiDocumentManager.getInstance(context.getProject()).commitDocument(document);
|
||||
newContext.set(CustomTemplateCallback.getContext(file, offset - 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
private static Condition<PostfixTemplate> createIsApplicationTemplateFunction(
|
||||
@NotNull PostfixTemplateProvider provider,
|
||||
@NotNull String key,
|
||||
@NotNull PsiFile file,
|
||||
@NotNull Editor editor) {
|
||||
int currentOffset = editor.getCaretModel().getOffset();
|
||||
final int newOffset = currentOffset - key.length();
|
||||
CharSequence fileContent = editor.getDocument().getCharsSequence();
|
||||
StringBuilder fileContentWithoutKey = new StringBuilder();
|
||||
fileContentWithoutKey.append(fileContent.subSequence(0, newOffset));
|
||||
fileContentWithoutKey.append(fileContent.subSequence(currentOffset, fileContent.length()));
|
||||
PsiFile copyFile = copyFile(file, fileContentWithoutKey);
|
||||
Document copyDocument = copyFile.getViewProvider().getDocument();
|
||||
if (copyDocument == null) {
|
||||
//noinspection unchecked
|
||||
return Condition.FALSE;
|
||||
}
|
||||
return newContext.get();
|
||||
|
||||
copyFile = provider.preCheck(editor, copyFile, newOffset);
|
||||
copyDocument = copyFile.getViewProvider().getDocument();
|
||||
if (copyDocument == null) {
|
||||
//noinspection unchecked
|
||||
return Condition.FALSE;
|
||||
}
|
||||
|
||||
final PsiElement context = CustomTemplateCallback.getContext(copyFile, newOffset > 0 ? newOffset - 1 : newOffset);
|
||||
final Document finalCopyDocument = copyDocument;
|
||||
return new Condition<PostfixTemplate>() {
|
||||
@Override
|
||||
public boolean value(PostfixTemplate template) {
|
||||
return template != null && template.isEnabled() && template.isApplicable(context, finalCopyDocument, newOffset);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isSemicolonNeeded(@NotNull PsiFile file, @NotNull Editor editor) {
|
||||
return JavaCompletionContributor.semicolonNeeded(editor, file, CompletionInitializationContext.calcStartOffset(editor));
|
||||
|
||||
@NotNull
|
||||
public static PsiFile copyFile(@NotNull PsiFile file, @NotNull StringBuilder fileContentWithoutKey) {
|
||||
final PsiFileFactory psiFileFactory = PsiFileFactory.getInstance(file.getProject());
|
||||
PsiFile copy = psiFileFactory.createFileFromText(file.getName(), file.getFileType(), fileContentWithoutKey);
|
||||
VirtualFile vFile = copy.getVirtualFile();
|
||||
if (vFile != null) {
|
||||
vFile.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
public static boolean isApplicableTemplate(
|
||||
@NotNull PostfixTemplateProvider provider,
|
||||
@NotNull String key,
|
||||
@NotNull PsiFile file,
|
||||
@NotNull Editor editor) {
|
||||
return createIsApplicationTemplateFunction(provider, key, file, editor).value(provider.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
-4
@@ -18,7 +18,6 @@ package com.intellij.codeInsight.template.postfix.templates;
|
||||
import com.intellij.codeInsight.template.postfix.settings.PostfixTemplatesSettings;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import com.intellij.psi.PsiExpressionStatement;
|
||||
@@ -32,9 +31,6 @@ public abstract class PostfixTemplate {
|
||||
@NotNull private final String myDescription;
|
||||
@NotNull private final String myExample;
|
||||
|
||||
@NotNull
|
||||
public static final ExtensionPointName<PostfixTemplate> EP_NAME = ExtensionPointName.create("com.intellij.postfixTemplate");
|
||||
|
||||
protected PostfixTemplate(@NotNull String name, @NotNull String description, @NotNull String example) {
|
||||
this(name, "." + name, description, example);
|
||||
}
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.intellij.codeInsight.template.postfix.templates;
|
||||
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface PostfixTemplateProvider {
|
||||
|
||||
/**
|
||||
* Return all templates registered in the provider
|
||||
*/
|
||||
@NotNull
|
||||
Set<PostfixTemplate> getTemplates();
|
||||
|
||||
/**
|
||||
* Return all keys registered in the provider
|
||||
*/
|
||||
@NotNull
|
||||
Set<String> getKeys();
|
||||
|
||||
/**
|
||||
* Return postfix template for key
|
||||
*/
|
||||
@Nullable
|
||||
PostfixTemplate get(@Nullable String key);
|
||||
|
||||
/**
|
||||
* Check symbol can separate template keys
|
||||
*/
|
||||
boolean isTerminalSymbol(char currentChar);
|
||||
|
||||
/**
|
||||
* Prepare original file content for template expanding
|
||||
*/
|
||||
@NotNull
|
||||
PsiElement preExpand(@NotNull Editor editor, @NotNull PsiElement context, int currentOffset, @NotNull String key);
|
||||
|
||||
/**
|
||||
* Do some actions with the file content before check applicable.
|
||||
* Return new copy of file because we should not change original file
|
||||
*/
|
||||
@NotNull
|
||||
PsiFile preCheck(@NotNull Editor editor, @NotNull PsiFile file, int currentOffset);
|
||||
}
|
||||
@@ -1,41 +1,23 @@
|
||||
<idea-plugin version="2">
|
||||
<extensionPoints>
|
||||
<extensionPoint name="postfixTemplate" interface="com.intellij.codeInsight.template.postfix.templates.PostfixTemplate"/>
|
||||
<extensionPoint name="codeInsight.template.postfixTemplateProvider"
|
||||
beanClass="com.intellij.lang.LanguageExtensionPoint">
|
||||
<with attribute="implementationClass" implements="com.intellij.codeInsight.template.postfix.templates.PostfixTemplateProvider"/>
|
||||
</extensionPoint>
|
||||
</extensionPoints>
|
||||
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.ElseStatementPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.IntroduceVariablePostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.NotNullCheckPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.IsNullCheckPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.NotExpressionPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.ForeachPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.WhileStatementPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.CastExpressionPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.IfStatementPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.ForAscendingPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.ForDescendingPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.IntroduceFieldPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.ParenthesizedExpressionPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.ReturnStatementPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.SwitchStatementPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.ThrowExceptionPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.AssertStatementPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.SynchronizedStatementPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.InstanceofExpressionPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.SoutPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.FormatPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.TryStatementPostfixTemplate"/>
|
||||
<postfixTemplate implementation="com.intellij.codeInsight.template.postfix.templates.TryWithResourcesPostfixTemplate"/>
|
||||
<codeInsight.template.postfixTemplateProvider language="JAVA" implementationClass="com.intellij.codeInsight.template.postfix.templates.JavaPostfixTemplateProvider"/>
|
||||
</extensions>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<applicationService id="postfixCompletionSettings" serviceImplementation="com.intellij.codeInsight.template.postfix.settings.PostfixTemplatesSettings"/>
|
||||
<customLiveTemplate implementation="com.intellij.codeInsight.template.postfix.templates.PostfixLiveTemplate"/>
|
||||
|
||||
|
||||
<completion.contributor language="JAVA" implementationClass="com.intellij.codeInsight.template.postfix.completion.PostfixTemplateCompletionContributor"/>
|
||||
<lookup.actionProvider implementation="com.intellij.codeInsight.template.postfix.completion.PostfixTemplateLookupActionProvider"/>
|
||||
|
||||
|
||||
<editorOptionsProvider id="editing.postfixCompletion" instance="com.intellij.codeInsight.template.postfix.settings.PostfixTemplatesConfigurable"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
Reference in New Issue
Block a user