mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
move special insertion logic in live templates to a dedicated lookup element decorator
This commit is contained in:
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2000-2012 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.impl;
|
||||
|
||||
import com.intellij.codeInsight.AutoPopupController;
|
||||
import com.intellij.codeInsight.completion.CompletionInitializationContext;
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.completion.OffsetMap;
|
||||
import com.intellij.codeInsight.completion.PrioritizedLookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator;
|
||||
import com.intellij.codeInsight.lookup.LookupItem;
|
||||
import com.intellij.codeInsight.template.TemplateLookupSelectionHandler;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorModificationUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
class TemplateExpressionLookupElement extends LookupElementDecorator<LookupElement> {
|
||||
private TemplateState myState;
|
||||
|
||||
public TemplateExpressionLookupElement(final TemplateState state, LookupElement element, int index) {
|
||||
super(PrioritizedLookupElement.withPriority(element, Integer.MAX_VALUE - 10 - index));
|
||||
myState = state;
|
||||
}
|
||||
|
||||
private static InsertionContext createInsertionContext(LookupElement item,
|
||||
PsiFile psiFile,
|
||||
List<? extends LookupElement> elements,
|
||||
Editor editor) {
|
||||
final OffsetMap offsetMap = new OffsetMap(editor.getDocument());
|
||||
final InsertionContext context = new InsertionContext(offsetMap, (char)0, elements.toArray(new LookupElement[elements.size()]), psiFile, editor, false);
|
||||
context.setTailOffset(editor.getCaretModel().getOffset());
|
||||
offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, context.getTailOffset() - item.getLookupString().length());
|
||||
offsetMap.addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, context.getTailOffset());
|
||||
offsetMap.addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, context.getTailOffset());
|
||||
return context;
|
||||
}
|
||||
|
||||
void handleTemplateInsert(List<? extends LookupElement> elements) {
|
||||
InsertionContext context = createInsertionContext(this, myState.getPsiFile(), elements, myState.getEditor());
|
||||
handleInsert(context);
|
||||
Disposer.dispose(context.getOffsetMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInsert(InsertionContext context) {
|
||||
LookupElement item = getDelegate();
|
||||
Project project = context.getProject();
|
||||
Editor editor = context.getEditor();
|
||||
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments();
|
||||
|
||||
TextRange range = myState.getCurrentVariableRange();
|
||||
final TemplateLookupSelectionHandler handler =
|
||||
item instanceof LookupItem ? ((LookupItem<?>)item).getAttribute(TemplateLookupSelectionHandler.KEY_IN_LOOKUP_ITEM) : null;
|
||||
if (handler != null && range != null) {
|
||||
handler.itemSelected(item, context.getFile(), context.getDocument(), range.getStartOffset(), range.getEndOffset());
|
||||
}
|
||||
else {
|
||||
super.handleInsert(context);
|
||||
}
|
||||
|
||||
if (context.getCompletionChar() == '.') {
|
||||
EditorModificationUtil.insertStringAtCaret(editor, ".");
|
||||
AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!myState.isFinished()) {
|
||||
myState.calcResults(true);
|
||||
}
|
||||
|
||||
myState.nextTab();
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,11 @@
|
||||
|
||||
package com.intellij.codeInsight.template.impl;
|
||||
|
||||
import com.intellij.codeInsight.AutoPopupController;
|
||||
import com.intellij.codeInsight.CodeInsightSettings;
|
||||
import com.intellij.codeInsight.completion.CompletionInitializationContext;
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.completion.OffsetMap;
|
||||
import com.intellij.codeInsight.lookup.*;
|
||||
import com.intellij.codeInsight.lookup.LookupAdapter;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupEvent;
|
||||
import com.intellij.codeInsight.lookup.LookupManager;
|
||||
import com.intellij.codeInsight.lookup.impl.LookupImpl;
|
||||
import com.intellij.codeInsight.template.*;
|
||||
import com.intellij.lang.LanguageLiteralEscapers;
|
||||
@@ -31,7 +30,6 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandAdapter;
|
||||
import com.intellij.openapi.command.CommandEvent;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.command.undo.BasicUndoableAction;
|
||||
import com.intellij.openapi.command.undo.DocumentReference;
|
||||
import com.intellij.openapi.command.undo.DocumentReferenceManager;
|
||||
@@ -44,7 +42,6 @@ import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.markup.*;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -57,6 +54,7 @@ import com.intellij.psi.util.PsiUtilBase;
|
||||
import com.intellij.refactoring.rename.inplace.InplaceRefactoring;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.PairProcessor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.containers.IntArrayList;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -313,7 +311,7 @@ public class TemplateState implements Disposable {
|
||||
myTemplateRange = myDocument.createRangeMarker(caretOffset, caretOffset + template.getTemplateText().length());
|
||||
}
|
||||
else {
|
||||
PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(myDocument);
|
||||
PsiFile file = getPsiFile();
|
||||
preprocessTemplate(file, myEditor.getCaretModel().getOffset(), myTemplate.getTemplateText());
|
||||
int caretOffset = myEditor.getCaretModel().getOffset();
|
||||
myTemplateRange = myDocument.createRangeMarker(caretOffset, caretOffset);
|
||||
@@ -402,7 +400,7 @@ public class TemplateState implements Disposable {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(myDocument);
|
||||
final PsiFile file = getPsiFile();
|
||||
if (file != null) {
|
||||
IntArrayList indices = initEmptyVariables();
|
||||
mySegments.setSegmentsGreedy(false);
|
||||
@@ -486,26 +484,23 @@ public class TemplateState implements Disposable {
|
||||
myEditor.getSelectionModel().setSelection(start, end);
|
||||
}
|
||||
|
||||
Expression expressionNode = myTemplate.getExpressionAt(myCurrentVariableNumber);
|
||||
final ExpressionContext context = createExpressionContext(start);
|
||||
final LookupElement[] lookupItems = expressionNode.calculateLookupItems(context);
|
||||
final PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myDocument);
|
||||
if (lookupItems != null && lookupItems.length > 0) {
|
||||
Expression expressionNode = getCurrentExpression();
|
||||
final List<TemplateExpressionLookupElement> lookupItems = getCurrentExpressionLookupItems();
|
||||
final PsiFile psiFile = getPsiFile();
|
||||
if (!lookupItems.isEmpty()) {
|
||||
if (((TemplateManagerImpl)TemplateManager.getInstance(myProject)).shouldSkipInTests()) {
|
||||
final String s = lookupItems[0].getLookupString();
|
||||
EditorModificationUtil.insertStringAtCaret(myEditor, s);
|
||||
itemSelected(lookupItems[0], psiFile, currentSegmentNumber, ' ', lookupItems);
|
||||
insertSingleItem(lookupItems);
|
||||
}
|
||||
else {
|
||||
for (LookupElement lookupItem : lookupItems) {
|
||||
assert lookupItem != null : expressionNode;
|
||||
}
|
||||
|
||||
runLookup(currentSegmentNumber, lookupItems, expressionNode.getAdvertisingText(), psiFile);
|
||||
runLookup(lookupItems, expressionNode.getAdvertisingText());
|
||||
}
|
||||
}
|
||||
else {
|
||||
Result result = expressionNode.calculateResult(context);
|
||||
Result result = expressionNode.calculateResult(getCurrentExpressionContext());
|
||||
if (result != null) {
|
||||
result.handleFocused(psiFile, myDocument, mySegments.getSegmentStart(currentSegmentNumber),
|
||||
mySegments.getSegmentEnd(currentSegmentNumber));
|
||||
@@ -514,12 +509,42 @@ public class TemplateState implements Disposable {
|
||||
focusCurrentHighlighter(true);
|
||||
}
|
||||
|
||||
private void runLookup(final int currentSegmentNumber, final LookupElement[] lookupItems, String advertisingText, final PsiFile psiFile) {
|
||||
PsiFile getPsiFile() {
|
||||
return PsiDocumentManager.getInstance(myProject).getPsiFile(myDocument);
|
||||
}
|
||||
|
||||
private void insertSingleItem(List<TemplateExpressionLookupElement> lookupItems) {
|
||||
TemplateExpressionLookupElement first = lookupItems.get(0);
|
||||
EditorModificationUtil.insertStringAtCaret(myEditor, first.getLookupString());
|
||||
first.handleTemplateInsert(lookupItems);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
List<TemplateExpressionLookupElement> getCurrentExpressionLookupItems() {
|
||||
LookupElement[] elements = getCurrentExpression().calculateLookupItems(getCurrentExpressionContext());
|
||||
if (elements == null) return Collections.emptyList();
|
||||
|
||||
List<TemplateExpressionLookupElement> result = ContainerUtil.newArrayList();
|
||||
for (int i = 0; i < elements.length; i++) {
|
||||
result.add(new TemplateExpressionLookupElement(this, elements[i], i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ExpressionContext getCurrentExpressionContext() {
|
||||
return createExpressionContext(mySegments.getSegmentStart(getCurrentSegmentNumber()));
|
||||
}
|
||||
|
||||
Expression getCurrentExpression() {
|
||||
return myTemplate.getExpressionAt(myCurrentVariableNumber);
|
||||
}
|
||||
|
||||
private void runLookup(final List<TemplateExpressionLookupElement> lookupItems, String advertisingText) {
|
||||
if (myEditor == null) return;
|
||||
|
||||
final LookupManager lookupManager = LookupManager.getInstance(myProject);
|
||||
|
||||
final LookupImpl lookup = (LookupImpl)lookupManager.showLookup(myEditor, lookupItems);
|
||||
final LookupImpl lookup = (LookupImpl)lookupManager.showLookup(myEditor, lookupItems.toArray(new LookupElement[lookupItems.size()]));
|
||||
if (lookup == null) return;
|
||||
|
||||
if (CodeInsightSettings.getInstance().AUTO_POPUP_COMPLETION_LOOKUP && myEditor.getUserData(InplaceRefactoring.INPLACE_RENAMER) == null) {
|
||||
@@ -542,68 +567,21 @@ public class TemplateState implements Disposable {
|
||||
if (isFinished()) return;
|
||||
ourLookupShown = false;
|
||||
|
||||
TemplateState.this.itemSelected(event.getItem(), psiFile, currentSegmentNumber, event.getCompletionChar(), lookupItems);
|
||||
LookupElement item = event.getItem();
|
||||
if (item instanceof TemplateExpressionLookupElement) {
|
||||
((TemplateExpressionLookupElement)item).handleTemplateInsert(lookupItems);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void itemSelected(final LookupElement item,
|
||||
final PsiFile psiFile,
|
||||
final int currentSegmentNumber,
|
||||
final char completionChar,
|
||||
LookupElement[] elements) {
|
||||
if (item != null) {
|
||||
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
|
||||
|
||||
final OffsetMap offsetMap = new OffsetMap(myDocument);
|
||||
final InsertionContext context = new InsertionContext(offsetMap, (char)0, elements, psiFile, myEditor, false);
|
||||
context.setTailOffset(myEditor.getCaretModel().getOffset());
|
||||
offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, context.getTailOffset() - item.getLookupString().length());
|
||||
offsetMap.addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, context.getTailOffset());
|
||||
offsetMap.addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, context.getTailOffset());
|
||||
|
||||
final TemplateLookupSelectionHandler handler =
|
||||
item instanceof LookupItem ? ((LookupItem<?>)item).getAttribute(TemplateLookupSelectionHandler.KEY_IN_LOOKUP_ITEM) : null;
|
||||
if (handler != null) {
|
||||
handler.itemSelected(item, psiFile, myDocument, mySegments.getSegmentStart(currentSegmentNumber),
|
||||
mySegments.getSegmentEnd(currentSegmentNumber));
|
||||
}
|
||||
else {
|
||||
new WriteCommandAction(myProject) {
|
||||
@Override
|
||||
protected void run(com.intellij.openapi.application.Result result) throws Throwable {
|
||||
item.handleInsert(context);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
Disposer.dispose(offsetMap);
|
||||
|
||||
if (completionChar == '.') {
|
||||
EditorModificationUtil.insertStringAtCaret(myEditor, ".");
|
||||
AutoPopupController.getInstance(myProject).autoPopupMemberLookup(myEditor, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isFinished()) {
|
||||
calcResults(true);
|
||||
}
|
||||
}
|
||||
|
||||
new WriteCommandAction(myProject) {
|
||||
@Override
|
||||
protected void run(com.intellij.openapi.application.Result result) throws Throwable {
|
||||
nextTab();
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void unblockDocument() {
|
||||
PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
|
||||
PsiDocumentManager.getInstance(myProject).doPostponedOperationsAndUnblockDocument(myDocument);
|
||||
}
|
||||
|
||||
// Hours spent fixing code : 1
|
||||
private void calcResults(final boolean isQuick) {
|
||||
void calcResults(final boolean isQuick) {
|
||||
if (myProcessor != null && myCurrentVariableNumber >= 0) {
|
||||
final String variableName = myTemplate.getVariableNameAt(myCurrentVariableNumber);
|
||||
final TextResult value = getVariableValue(variableName);
|
||||
@@ -672,7 +650,7 @@ public class TemplateState implements Disposable {
|
||||
int end = mySegments.getSegmentEnd(segmentNumber);
|
||||
|
||||
PsiDocumentManager.getInstance(myProject).commitDocument(myDocument);
|
||||
PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myDocument);
|
||||
PsiFile psiFile = getPsiFile();
|
||||
PsiElement element = psiFile.findElementAt(start);
|
||||
|
||||
ExpressionContext context = createExpressionContext(start);
|
||||
@@ -1012,7 +990,7 @@ public class TemplateState implements Disposable {
|
||||
}
|
||||
|
||||
private void reformat(RangeMarker rangeMarkerToReformat) {
|
||||
final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(myDocument);
|
||||
final PsiFile file = getPsiFile();
|
||||
if (file != null) {
|
||||
CodeStyleManager style = CodeStyleManager.getInstance(myProject);
|
||||
for (TemplateOptionalProcessor optionalProcessor : Extensions.getExtensions(TemplateOptionalProcessor.EP_NAME)) {
|
||||
|
||||
Reference in New Issue
Block a user