mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
completion preview initial
This commit is contained in:
@@ -130,7 +130,6 @@ public class LookupElementPresentation {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Deprecated
|
||||
public String getTailText() {
|
||||
if (myTail == null) return null;
|
||||
return StringUtil.join(myTail, new Function<TextFragment, String>() {
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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.completion;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation;
|
||||
import com.intellij.codeInsight.lookup.impl.LookupCellRenderer;
|
||||
import com.intellij.codeInsight.lookup.impl.LookupImpl;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.AccessToken;
|
||||
import com.intellij.openapi.application.WriteAction;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer;
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea;
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class CompletionPreview {
|
||||
private final LookupImpl myLookup;
|
||||
private Disposable myUninstaller;
|
||||
private int myPreviewStart;
|
||||
|
||||
public CompletionPreview(LookupImpl lookup) {
|
||||
myLookup = lookup;
|
||||
}
|
||||
|
||||
public void installPreview() {
|
||||
List<LookupElement> items = myLookup.getItems();
|
||||
if (items.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LookupElement first = items.get(0);
|
||||
final String text = getPreviewText(first);
|
||||
if (text == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String prefix = myLookup.itemPattern(first);
|
||||
final Iterable<TextRange> fragments = LookupCellRenderer.getMatchingFragments(prefix, text);
|
||||
if (fragments == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<TextRange> ranges = ContainerUtil.newArrayList(fragments);
|
||||
if (ranges.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int lastMatch = ranges.get(ranges.size() - 1).getEndOffset();
|
||||
|
||||
final Editor editor = myLookup.getEditor();
|
||||
final int caret = editor.getCaretModel().getOffset();
|
||||
myPreviewStart = caret - prefix.length();
|
||||
final int previewCaret = myPreviewStart + lastMatch;
|
||||
final int previewEnd = myPreviewStart + text.length();
|
||||
|
||||
final List<RangeHighlighter> highlighters = ContainerUtil.newArrayList();
|
||||
myLookup.performGuardedChange(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
AccessToken token = WriteAction.start();
|
||||
try {
|
||||
editor.getDocument().insertString(caret, text.substring(lastMatch));
|
||||
editor.getDocument().replaceString(myPreviewStart, caret, text.substring(0, lastMatch));
|
||||
editor.getCaretModel().moveToOffset(previewCaret);
|
||||
}
|
||||
finally {
|
||||
token.finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
CommandProcessor.getInstance().runUndoTransparentAction(runnable);
|
||||
|
||||
int lastOffset = 0;
|
||||
for (TextRange range : fragments) {
|
||||
if (range.getStartOffset() > lastOffset) {
|
||||
highlighters.add(createRange(lastOffset, range.getStartOffset(), true));
|
||||
}
|
||||
highlighters.add(createRange(range.getStartOffset(), range.getEndOffset(), false));
|
||||
lastOffset = range.getEndOffset();
|
||||
}
|
||||
if (lastOffset < text.length()) {
|
||||
highlighters.add(createRange(lastOffset, text.length(), true));
|
||||
}
|
||||
|
||||
}
|
||||
}, "preview");
|
||||
myLookup.setPreview(this);
|
||||
|
||||
myUninstaller = new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
myLookup.setPreview(null);
|
||||
myUninstaller = null;
|
||||
|
||||
if (editor.isDisposed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (RangeHighlighter highlighter : highlighters) {
|
||||
editor.getMarkupModel().removeHighlighter(highlighter);
|
||||
}
|
||||
|
||||
myLookup.performGuardedChange(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
AccessToken token = WriteAction.start();
|
||||
try {
|
||||
editor.getDocument().replaceString(myPreviewStart, previewEnd, prefix);
|
||||
editor.getCaretModel().moveToOffset(myPreviewStart + prefix.length());
|
||||
}
|
||||
finally {
|
||||
token.finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
CommandProcessor.getInstance().runUndoTransparentAction(runnable);
|
||||
}
|
||||
}, "remove preview");
|
||||
}
|
||||
};
|
||||
Disposer.register(myLookup, myUninstaller);
|
||||
}
|
||||
|
||||
private RangeHighlighter createRange(final int start, final int end, boolean generated) {
|
||||
return myLookup.getEditor().getMarkupModel().addRangeHighlighter(myPreviewStart + start, myPreviewStart + end, HighlighterLayer.LAST,
|
||||
new TextAttributes(generated ? Color.LIGHT_GRAY : Color.BLACK, null, null, null, Font.PLAIN),
|
||||
HighlighterTargetArea.EXACT_RANGE);
|
||||
}
|
||||
|
||||
public void uninstallPreview() {
|
||||
if (myUninstaller != null) {
|
||||
Disposer.dispose(myUninstaller);
|
||||
assert myUninstaller == null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getPreviewText(LookupElement item) {
|
||||
LookupElementPresentation presentation = LookupElementPresentation.renderElement(item);
|
||||
String text = presentation.getItemText();
|
||||
if (text == null) {
|
||||
return null;
|
||||
}
|
||||
String tailText = presentation.getTailText();
|
||||
if (tailText != null && tailText.startsWith("(") && tailText.contains(")")) {
|
||||
text += "()";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -67,7 +67,6 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import com.intellij.util.ui.update.MergingUpdateQueue;
|
||||
import com.intellij.util.ui.update.Update;
|
||||
import gnu.trove.TObjectHashingStrategy;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
@@ -118,6 +117,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement
|
||||
}
|
||||
};
|
||||
private volatile int myCount;
|
||||
private volatile boolean myShowPreview;
|
||||
private final ConcurrentHashMap<LookupElement, CompletionSorterImpl> myItemSorters = new ConcurrentHashMap<LookupElement, CompletionSorterImpl>(
|
||||
ContainerUtil.<LookupElement>identityStrategy());
|
||||
private final PropertyChangeListener myLookupManagerListener;
|
||||
@@ -170,7 +170,12 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement
|
||||
void duringCompletion(CompletionInitializationContext initContext) {
|
||||
if (isAutopopupCompletion()) {
|
||||
if (shouldFocusLookup(myParameters)) {
|
||||
myLookup.setFocused(true);
|
||||
if (Registry.is("ide.completion.show.preview")) {
|
||||
myShowPreview = true;
|
||||
myLookup.setFocused(false);
|
||||
} else {
|
||||
myLookup.setFocused(true);
|
||||
}
|
||||
} else if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(CodeCompletionFeatures.EDITING_COMPLETION_CONTROL_ENTER, getProject())) {
|
||||
myLookup.addAdvertisement("Press " +
|
||||
CompletionContributor.getActionShortcut(IdeActions.ACTION_CHOOSE_LOOKUP_ITEM_ALWAYS) +
|
||||
@@ -328,6 +333,9 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement
|
||||
if (!myLookup.showLookup()) {
|
||||
return false;
|
||||
}
|
||||
if (myShowPreview) {
|
||||
new CompletionPreview(myLookup).installPreview();
|
||||
}
|
||||
justShown = true;
|
||||
}
|
||||
myLookup.refreshUi(true, justShown);
|
||||
|
||||
@@ -292,7 +292,7 @@ public class LookupCellRenderer implements ListCellRenderer {
|
||||
|
||||
final String prefix = item instanceof EmptyLookupItem ? "" : myLookup.itemPattern(item);
|
||||
if (prefix.length() > 0) {
|
||||
Iterable<TextRange> ranges = new MinusculeMatcher("*" + prefix, NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(name);
|
||||
Iterable<TextRange> ranges = getMatchingFragments(prefix, name);
|
||||
if (ranges != null) {
|
||||
SimpleTextAttributes highlighted =
|
||||
new SimpleTextAttributes(style, selected ? SELECTED_PREFIX_FOREGROUND_COLOR : PREFIX_FOREGROUND_COLOR);
|
||||
@@ -303,6 +303,10 @@ public class LookupCellRenderer implements ListCellRenderer {
|
||||
nameComponent.append(name, base);
|
||||
}
|
||||
|
||||
public static Iterable<TextRange> getMatchingFragments(String prefix, String name) {
|
||||
return new MinusculeMatcher("*" + prefix, NameUtil.MatchingCaseSensitivity.NONE).matchingFragments(name);
|
||||
}
|
||||
|
||||
private int setTypeTextLabel(LookupElement item,
|
||||
final Color background,
|
||||
Color foreground,
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
package com.intellij.codeInsight.lookup.impl;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.completion.CodeCompletionFeatures;
|
||||
import com.intellij.codeInsight.completion.CompletionLookupArranger;
|
||||
import com.intellij.codeInsight.completion.PrefixMatcher;
|
||||
import com.intellij.codeInsight.completion.ShowHideIntentionIconLookupAction;
|
||||
import com.intellij.codeInsight.completion.*;
|
||||
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
|
||||
import com.intellij.codeInsight.hint.HintManager;
|
||||
import com.intellij.codeInsight.hint.HintManagerImpl;
|
||||
@@ -150,6 +147,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
|
||||
private int myMaximumHeight = Integer.MAX_VALUE;
|
||||
private boolean myFinishing;
|
||||
private boolean myUpdating;
|
||||
private CompletionPreview myPreview;
|
||||
private final ModalityState myModalityState;
|
||||
|
||||
public LookupImpl(Project project, Editor editor, @NotNull LookupArranger arranger) {
|
||||
@@ -728,6 +726,10 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
|
||||
public boolean performGuardedChange(Runnable change, @Nullable final String debug) {
|
||||
checkValid();
|
||||
assert !myChangeGuard : "already in change";
|
||||
if (myPreview != null) {
|
||||
myPreview.uninstallPreview();
|
||||
assert myPreview == null;
|
||||
}
|
||||
|
||||
myChangeGuard = true;
|
||||
boolean result;
|
||||
@@ -1467,4 +1469,12 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
|
||||
return myPresentableArranger.getRelevanceStrings();
|
||||
}
|
||||
}
|
||||
|
||||
public CompletionPreview getPreview() {
|
||||
return myPreview;
|
||||
}
|
||||
|
||||
public void setPreview(CompletionPreview preview) {
|
||||
myPreview = preview;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +196,10 @@ ide.completion.allow.finishing.by.chars=true
|
||||
# suppress inspection "UnusedProperty"
|
||||
ide.completion.allow.finishing.by.chars.description=Controls whether typing dot, space, etc. may select the current lookup item
|
||||
|
||||
ide.completion.show.preview=false
|
||||
# suppress inspection "UnusedProperty"
|
||||
ide.completion.show.preview.description=Controls whether the completion autopopup should show its first result as a preview in editor
|
||||
|
||||
ide.completion.middle.matching=true
|
||||
# suppress inspection "UnusedProperty"
|
||||
ide.completion.middle.matching.description=Suggest items in completion that contain the entered string somewhere in the middle.
|
||||
|
||||
Reference in New Issue
Block a user