extract LookupOffsets

This commit is contained in:
peter
2012-08-07 19:07:44 +02:00
parent 0695aa715e
commit bd83dfa330
2 changed files with 179 additions and 86 deletions
@@ -91,13 +91,9 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
private static final Icon relevanceSortIcon = AllIcons.Ide.LookupRelevance;
private static final Icon lexiSortIcon = AllIcons.Ide.LookupAlphanumeric;
private final LookupOffsets myOffsets;
private final Project myProject;
private final Editor myEditor;
private String myInitialPrefix;
private boolean myStableStart;
private RangeMarker myLookupStartMarker;
private RangeMarker myLookupOriginalStartMarker;
private final JBList myList = new JBList(new CollectionListModel<LookupElement>()) {
@Override
protected void processKeyEvent(final KeyEvent e) {
@@ -133,7 +129,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
private boolean myHidden = false;
private boolean mySelectionTouched;
private boolean myFocused = true;
private String myAdditionalPrefix = "";
private final AsyncProcessIcon myProcessIcon = new AsyncProcessIcon("Completion progress");
private final JPanel myIconPanel = new JPanel(new BorderLayout());
private volatile boolean myCalculating;
@@ -210,11 +205,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
myIconPanel.setBackground(Color.LIGHT_GRAY);
myIconPanel.add(myProcessIcon);
updateLookupStart(0);
int caret = myEditor.getCaretModel().getOffset();
myLookupOriginalStartMarker = myEditor.getDocument().createRangeMarker(caret, caret);
myLookupOriginalStartMarker.setGreedyToLeft(true);
myOffsets = new LookupOffsets(editor);
final CollectionListModel<LookupElement> model = getListModel();
addEmptyItem(model);
@@ -395,13 +386,12 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
public String getAdditionalPrefix() {
return myAdditionalPrefix;
return myOffsets.getAdditionalPrefix();
}
void appendPrefix(char c) {
checkValid();
myAdditionalPrefix += c;
myInitialPrefix = null;
myOffsets.appendPrefix(c);
synchronized (myList) {
myPresentableArranger.prefixChanged();
}
@@ -425,15 +415,14 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
}
boolean truncatePrefix(boolean preserveSelection) {
final int len = myAdditionalPrefix.length();
if (len == 0) return false;
if (!myOffsets.truncatePrefix()) {
return false;
}
if (preserveSelection) {
markSelectionTouched();
}
myAdditionalPrefix = myAdditionalPrefix.substring(0, len - 1);
myInitialPrefix = null;
boolean shouldUpdate;
synchronized (myList) {
shouldUpdate = myPresentableArranger == myArranger;
@@ -463,7 +452,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
LOG.error("Arranger " + myPresentableArranger + " returned invalid selection index=" + toSelect + "; items=" + items);
}
checkMinPrefixLengthChanges(items);
myOffsets.checkMinPrefixLengthChanges(items, this);
List<LookupElement> oldModel = listModel.toList();
listModel.removeAll();
@@ -497,20 +486,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
}
}
private void checkMinPrefixLengthChanges(Collection<LookupElement> items) {
if (myStableStart) return;
if (!myCalculating && !items.isEmpty()) {
myStableStart = true;
}
int minPrefixLength = items.isEmpty() ? 0 : Integer.MAX_VALUE;
for (final LookupElement item : items) {
minPrefixLength = Math.min(itemMatcher(item).getPrefix().length(), minPrefixLength);
}
updateLookupStart(minPrefixLength);
}
private void updateListHeight(ListModel model) {
myList.setFixedCellHeight(myCellRenderer.getListCellRendererComponent(myList, model.getElementAt(0), 0, false, false).getPreferredSize().height);
@@ -536,7 +511,8 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
@Override
public String itemPattern(@NotNull LookupElement element) {
String prefix = itemMatcher(element).getPrefix();
return myAdditionalPrefix.isEmpty() ? prefix : prefix + myAdditionalPrefix;
String additionalPrefix = getAdditionalPrefix();
return additionalPrefix.isEmpty() ? prefix : prefix + additionalPrefix;
}
@Override
@@ -546,7 +522,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
return false;
}
if (myAdditionalPrefix.length() > 0) {
if (getAdditionalPrefix().length() > 0) {
matcher = matcher.cloneWithPrefix(itemPattern(item));
}
return matcher.prefixMatches(item);
@@ -740,12 +716,11 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
}
public int getLookupStart() {
LOG.assertTrue(myLookupStartMarker.isValid(), disposeTrace);
return myLookupStartMarker.getStartOffset();
return myOffsets.getLookupStart(disposeTrace);
}
public int getLookupOriginalStart() {
return myLookupOriginalStartMarker.isValid() ? myLookupOriginalStartMarker.getStartOffset() : -1;
return myOffsets.getLookupOriginalStart();
}
public boolean performGuardedChange(Runnable change) {
@@ -754,29 +729,17 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
public boolean performGuardedChange(Runnable change, @Nullable final String debug) {
checkValid();
assert myLookupStartMarker != null : "null start before";
assert myLookupStartMarker.isValid() : "invalid start";
assert !myChangeGuard : "already in change";
myChangeGuard = true;
final Document document = myEditor.getDocument();
RangeMarkerSpy spy = new RangeMarkerSpy(myLookupStartMarker) {
@Override
protected void invalidated(DocumentEvent e) {
LOG.info("Lookup start marker invalidated, say thanks to the " + e +
", doc=" + document +
", debug=" + debug);
}
};
document.addDocumentListener(spy);
boolean result;
try {
change.run();
result = myOffsets.performGuardedChange(change, debug);
}
finally {
document.removeDocumentListener(spy);
myChangeGuard = false;
}
if (myDisposed || !myLookupStartMarker.isValid()) {
if (!result || myDisposed) {
hide();
return false;
}
@@ -958,22 +921,6 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
}
}
private int updateLookupStart(int myMinPrefixLength) {
int offset = myEditor.getSelectionModel().hasSelection()
? myEditor.getSelectionModel().getSelectionStart()
: myEditor.getCaretModel().getOffset();
int start = Math.max(offset - myMinPrefixLength - myAdditionalPrefix.length(), 0);
if (myLookupStartMarker != null) {
if (myLookupStartMarker.isValid() && myLookupStartMarker.getStartOffset() == start && myLookupStartMarker.getEndOffset() == start) {
return start;
}
myLookupStartMarker.dispose();
}
myLookupStartMarker = myEditor.getDocument().createRangeMarker(start, start);
myLookupStartMarker.setGreedyToLeft(true);
return start;
}
@Nullable
public LookupElement getCurrentItem(){
LookupElement item = (LookupElement)myList.getSelectedValue();
@@ -1069,7 +1016,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
final PrefixMatcher firstItemMatcher = itemMatcher(firstItem);
final String oldPrefix = firstItemMatcher.getPrefix();
final String presentPrefix = oldPrefix + myAdditionalPrefix;
final String presentPrefix = oldPrefix + getAdditionalPrefix();
String commonPrefix = getCaseCorrectedLookupString(firstItem);
for (int i = 1; i < listModel.getSize(); i++) {
@@ -1105,12 +1052,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
}
}
if (myAdditionalPrefix.length() == 0 && myInitialPrefix == null && !explicitlyInvoked) {
myInitialPrefix = presentPrefix;
}
else {
myInitialPrefix = null;
}
myOffsets.setInitialPrefix(presentPrefix, explicitlyInvoked);
replacePrefix(presentPrefix, commonPrefix);
return true;
@@ -1136,7 +1078,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
myMatchers.clear();
myMatchers.putAll(newMatchers);
myAdditionalPrefix = "";
myOffsets.clearAdditionalPrefix();
myEditor.getCaretModel().moveToOffset(start + newPrefix.length());
}
@@ -1219,9 +1161,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
}
public void restorePrefix() {
if (myInitialPrefix != null) {
myEditor.getDocument().replaceString(getLookupStart(), myEditor.getCaretModel().getOffset(), myInitialPrefix);
}
myOffsets.restorePrefix(getLookupStart());
}
private static String staticDisposeTrace = null;
@@ -1239,11 +1179,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
return;
}
if (myLookupStartMarker != null) {
myLookupStartMarker.dispose();
myLookupStartMarker = null;
}
myLookupOriginalStartMarker.dispose();
myOffsets.disposeMarkers();
Disposer.dispose(myProcessIcon);
Disposer.dispose(myHintAlarm);
myDisposed = true;
@@ -1255,7 +1191,7 @@ public class LookupImpl extends LightweightHint implements LookupEx, Disposable
public void refreshUi(boolean mayCheckReused, boolean onExplicitAction) {
final boolean reused = mayCheckReused && checkReused();
if (reused) {
myAdditionalPrefix = "";
myOffsets.clearAdditionalPrefix();
}
boolean selectionVisible = isSelectionVisible();
@@ -0,0 +1,157 @@
/*
* 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.lookup.impl;
import com.intellij.codeInsight.completion.RangeMarkerSpy;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.event.DocumentEvent;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
/**
* @author peter
*/
public class LookupOffsets {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.lookup.impl.LookupOffsets");
private String myAdditionalPrefix = "";
private String myInitialPrefix;
private boolean myStableStart;
private RangeMarker myLookupStartMarker;
private RangeMarker myLookupOriginalStartMarker;
private final Editor myEditor;
public LookupOffsets(Editor editor) {
myEditor = editor;
int caret = editor.getCaretModel().getOffset();
myLookupOriginalStartMarker = editor.getDocument().createRangeMarker(caret, caret);
myLookupOriginalStartMarker.setGreedyToLeft(true);
updateLookupStart(0);
}
int updateLookupStart(int myMinPrefixLength) {
int offset = myEditor.getSelectionModel().hasSelection()
? myEditor.getSelectionModel().getSelectionStart()
: myEditor.getCaretModel().getOffset();
int start = Math.max(offset - myMinPrefixLength - myAdditionalPrefix.length(), 0);
if (myLookupStartMarker != null) {
if (myLookupStartMarker.isValid() && myLookupStartMarker.getStartOffset() == start && myLookupStartMarker.getEndOffset() == start) {
return start;
}
myLookupStartMarker.dispose();
}
myLookupStartMarker = myEditor.getDocument().createRangeMarker(start, start);
myLookupStartMarker.setGreedyToLeft(true);
return start;
}
public String getAdditionalPrefix() {
return myAdditionalPrefix;
}
public void appendPrefix(char c) {
myAdditionalPrefix += c;
myInitialPrefix = null;
}
public boolean truncatePrefix() {
final int len = myAdditionalPrefix.length();
if (len == 0) {
return false;
}
myAdditionalPrefix = myAdditionalPrefix.substring(0, len - 1);
myInitialPrefix = null;
return true;
}
void checkMinPrefixLengthChanges(Collection<LookupElement> items, LookupImpl lookup) {
if (myStableStart) return;
if (!lookup.isCalculating() && !items.isEmpty()) {
myStableStart = true;
}
int minPrefixLength = items.isEmpty() ? 0 : Integer.MAX_VALUE;
for (final LookupElement item : items) {
minPrefixLength = Math.min(lookup.itemMatcher(item).getPrefix().length(), minPrefixLength);
}
updateLookupStart(minPrefixLength);
}
public int getLookupStart(String disposeTrace) {
LOG.assertTrue(myLookupStartMarker.isValid(), disposeTrace);
return myLookupStartMarker.getStartOffset();
}
public int getLookupOriginalStart() {
return myLookupOriginalStartMarker.isValid() ? myLookupOriginalStartMarker.getStartOffset() : -1;
}
public boolean performGuardedChange(Runnable change, @Nullable final String debug) {
assert myLookupStartMarker != null : "null start before";
assert myLookupStartMarker.isValid() : "invalid start";
final Document document = myEditor.getDocument();
RangeMarkerSpy spy = new RangeMarkerSpy(myLookupStartMarker) {
@Override
protected void invalidated(DocumentEvent e) {
LOG.info("Lookup start marker invalidated, say thanks to the " + e +
", doc=" + document +
", debug=" + debug);
}
};
document.addDocumentListener(spy);
try {
change.run();
}
finally {
document.removeDocumentListener(spy);
}
return myLookupStartMarker.isValid();
}
public void setInitialPrefix(String presentPrefix, boolean explicitlyInvoked) {
if (myAdditionalPrefix.length() == 0 && myInitialPrefix == null && !explicitlyInvoked) {
myInitialPrefix = presentPrefix;
}
else {
myInitialPrefix = null;
}
}
public void clearAdditionalPrefix() {
myAdditionalPrefix = "";
}
void restorePrefix(int lookupStart) {
if (myInitialPrefix != null) {
myEditor.getDocument().replaceString(lookupStart, myEditor.getCaretModel().getOffset(), myInitialPrefix);
}
}
public void disposeMarkers() {
if (myLookupStartMarker != null) {
myLookupStartMarker.dispose();
myLookupStartMarker = null;
}
myLookupOriginalStartMarker.dispose();
}
}