IDEA-21987 idea is very slow after getting focus back

IDEA-91428 directory prefix is useless in "Show directory in editor tabs for non-unique filenames"
This commit is contained in:
Maxim.Mossienko
2012-12-06 09:05:26 +01:00
parent e83cece3c6
commit c35e30ee22
2 changed files with 94 additions and 133 deletions
@@ -18,30 +18,36 @@ public class UniqueNameBuilderTest extends TestCase {
UniqueNameBuilder<String> builder = new UniqueNameBuilder<String>("", "/", 100);
builder.addPath("A", "/Users/yole/idea/foo/buy/index.html");
builder.addPath("B", "/Users/yole/idea/bar/buy/index.html");
assertEquals("foo/buy/index.html", builder.getShortPath("A"));
assertEquals("foo/\u2026/index.html", builder.getShortPath("A"));
}
public void testSeparator() {
UniqueNameBuilder<String> builder = new UniqueNameBuilder<String>("", "\\", 100);
builder.addPath("A", "/Users/yole/idea/foo/buy/index.html");
builder.addPath("B", "/Users/yole/idea/bar/buy/index.html");
assertEquals("foo\\buy\\index.html", builder.getShortPath("A"));
assertEquals("foo\\\u2026\\index.html", builder.getShortPath("A"));
}
public void testRoot() {
UniqueNameBuilder<String> builder = new UniqueNameBuilder<String>("/Users/yole/idea", "/", 100);
builder.addPath("A", "/Users/yole/idea/build/scripts/layouts.gant");
builder.addPath("B", "/Users/yole/idea/community/build/scripts/layouts.gant");
assertEquals("build/scripts/layouts.gant", builder.getShortPath("A"));
assertEquals("community/build/scripts/layouts.gant", builder.getShortPath("B"));
assertEquals("build/\u2026/layouts.gant", builder.getShortPath("A"));
assertEquals("community/\u2026/layouts.gant", builder.getShortPath("B"));
builder = new UniqueNameBuilder<String>("", "/", 100);
builder.addPath("A", "build/scripts/layouts.gant");
builder.addPath("B", "community/build/scripts/layouts.gant");
assertEquals("build/\u2026/layouts.gant", builder.getShortPath("A"));
assertEquals("community/\u2026/layouts.gant", builder.getShortPath("B"));
}
public void testShortenNames() {
UniqueNameBuilder<String> builder = new UniqueNameBuilder<String>("/Users/yole/idea", "/", 25);
builder.addPath("A", "/Users/yole/idea/build/scripts/layouts.gant");
builder.addPath("B", "/Users/yole/idea/community/build/scripts/layouts.gant");
assertEquals("build/s\u2026/layouts.gant", builder.getShortPath("A"));
assertEquals("community/b\u2026/s\u2026/layouts.gant", builder.getShortPath("B"));
assertEquals("build/\u2026/layouts.gant", builder.getShortPath("A"));
assertEquals("community/\u2026/layouts.gant", builder.getShortPath("B"));
}
public void testShortenNamesUnique() {
@@ -49,6 +55,14 @@ public class UniqueNameBuilderTest extends TestCase {
builder.addPath("A", "/Users/yole/idea/pycharm/download/index.html");
builder.addPath("B", "/Users/yole/idea/pycharm/documentation/index.html");
builder.addPath("C", "/Users/yole/idea/fabrique/download/index.html");
assertEquals("pycharm/dow\u2026/index.html", builder.getShortPath("A"));
assertEquals("pycharm/\u2026/index.html", builder.getShortPath("A"));
}
public void testShortenNamesUnique2() {
UniqueNameBuilder<String> builder = new UniqueNameBuilder<String>("/Users/yole/idea", "/", 25);
builder.addPath("A", "source/components/views/something/tmpl/default.php");
builder.addPath("B", "source/components/views/something_else/tmpl/default.php");
assertEquals("something/\u2026/default.php", builder.getShortPath("A"));
assertEquals("something_else/\u2026/default.php", builder.getShortPath("B"));
}
}
@@ -1,7 +1,7 @@
package com.intellij.openapi.util.io;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.THashMap;
import gnu.trove.TIntObjectHashMap;
import java.util.*;
@@ -9,12 +9,11 @@ import java.util.*;
* @author yole
*/
public class UniqueNameBuilder<T> {
private final Map<T, String> myPaths = new HashMap<T, String>();
private final Map<T, String> myShortPaths = new HashMap<T, String>();
private int myAbbreviateLevel = 1;
private final String myRoot;
public static final char INTERNAL_PATH_DELIMITER = '/';
private final Map<T, String> myPaths = new THashMap<T, String>();
private final String mySeparator;
private final int myMaxLength;
private final String myRoot;
public UniqueNameBuilder(String root, String separator, int maxLength) {
myRoot = root;
@@ -22,134 +21,82 @@ public class UniqueNameBuilder<T> {
myMaxLength = maxLength;
}
private static class Node {
final char myChar;
final TIntObjectHashMap<Node> myChildren;
final Node myParentNode;
Node(char ch, Node parentNode) {
myChar = ch;
myParentNode = parentNode;
myChildren = new TIntObjectHashMap<Node>(1);
}
}
private final Node myRootNode = new Node('\0', null);
public void addPath(T key, String value) {
if (value.startsWith(myRoot)) value = value.substring(myRoot.length());
myPaths.put(key, value);
}
private void buildShortPaths() {
while (true) {
myShortPaths.clear();
if (!reabbreviate()) break;
myAbbreviateLevel++;
}
}
Node current = myRootNode;
private boolean reabbreviate() {
Map<T, List<String>> segmentLists = new HashMap<T, List<String>>();
for (Map.Entry<T, String> entry : myPaths.entrySet()) {
List<String> segments = splitIntoSegments(entry.getValue());
String shortPath = StringUtil.join(segments, mySeparator);
if (shortPath.length() <= myMaxLength) {
if (addShortPath(entry.getKey(), segments)) {
return true;
}
}
else {
segmentLists.put(entry.getKey(), segments);
}
for(int i = value.length() - 1; i >=0; --i) {
char ch = value.charAt(i);
Node node = current.myChildren.get(ch);
if (node == null) current.myChildren.put(ch, node = new Node(ch, current));
current = node;
}
truncateSegments(segmentLists);
for (Map.Entry<T, List<String>> entry : segmentLists.entrySet()) {
final List<String> segmentList = entry.getValue();
Collections.reverse(segmentList);
String shortPath = StringUtil.join(segmentList, mySeparator);
if (myShortPaths.containsValue(shortPath)) {
return true;
}
myShortPaths.put(entry.getKey(), shortPath);
}
return false;
}
private boolean addShortPath(final T key, List<String> segments) {
String shortPath;
Collections.reverse(segments);
shortPath = StringUtil.join(segments, mySeparator);
if (myShortPaths.containsValue(shortPath)) {
return true;
}
myShortPaths.put(key, shortPath);
return false;
}
private void truncateSegments(Map<T, List<String>> segmentLists) {
for(int i=1; i<myAbbreviateLevel; i++) {
tryTruncateSegments(segmentLists, i);
}
}
private static final String NON_UNIQUE = "::::::";
private void tryTruncateSegments(Map<T, List<String>> segmentLists, int index) {
Map<String, String> truncatedSegments = ContainerUtil.newHashMap();
for (Map.Entry<T, List<String>> entry : segmentLists.entrySet()) {
if (entry.getValue().size() <= index+1) {
continue;
}
String segment = entry.getValue().get(index);
for (int i = 1; i < segment.length(); i++) {
String truncated = segment.substring(0, i);
String existing = truncatedSegments.get(truncated);
if (existing == null) {
truncatedSegments.put(truncated, segment);
break;
}
else if (existing.equals(segment)) {
break;
}
else if (!existing.equals(segment) && existing != NON_UNIQUE) {
while (i < existing.length() && i < segment.length() && existing.charAt(i-1) == segment.charAt(i-1)) {
truncatedSegments.put(segment.substring(0, i), NON_UNIQUE);
//noinspection AssignmentToForLoopParameter
i++;
}
if (i < existing.length()) {
truncatedSegments.put(existing.substring(0, i), existing);
}
if (i < segment.length()) {
truncatedSegments.put(segment.substring(0, i), segment);
}
break;
}
}
}
Map<String, String> inverted = ContainerUtil.newHashMap();
for (Map.Entry<String, String> entry : truncatedSegments.entrySet()) {
if (entry.getValue() != NON_UNIQUE) {
inverted.put(entry.getValue(), entry.getKey());
}
}
for (Map.Entry<T, List<String>> entry : segmentLists.entrySet()) {
if (entry.getValue().size() <= index+1) {
continue;
}
String segment = entry.getValue().get(index);
String truncated = inverted.get(segment);
if (truncated != null && truncated.length() < segment.length()) {
entry.getValue().set(index, truncated + "\u2026");
}
}
}
private List<String> splitIntoSegments(String path) {
int pos = path.lastIndexOf('/');
if (pos < 0) return Collections.singletonList(path);
List<String> segments = new ArrayList<String>();
final String segment = path.substring(pos + 1);
segments.add(segment);
for (int i = 0; i < myAbbreviateLevel; i++) {
int prevPos = path.lastIndexOf('/', pos-1);
segments.add(path.substring(prevPos + 1, pos));
pos = prevPos;
if (pos < 0 || path.substring(0, pos).equals(myRoot)) break;
}
return segments;
}
public String getShortPath(T key) {
if (myShortPaths.isEmpty()) {
buildShortPaths();
String path = myPaths.get(key);
if (path == null) return key.toString();
Node current = myRootNode, firstDirNodeWithSingleChildAfterNodeWithManyChildren = null;
Node firstDirNode = null;
boolean searchingForManyChildren = current.myChildren.size() == 1;
for(int i = path.length() - 1; i >= 0; --i) {
Node node = current.myChildren.get(path.charAt(i));
if (node == null) return path;
if (firstDirNode == null && node.myChar == INTERNAL_PATH_DELIMITER) {
firstDirNode = node;
}
if (searchingForManyChildren && node.myChildren.size() > 1) {
searchingForManyChildren = false;
} else if (!searchingForManyChildren &&
firstDirNodeWithSingleChildAfterNodeWithManyChildren == null &&
node.myChildren.size() == 1 && node.myChar == INTERNAL_PATH_DELIMITER) {
firstDirNodeWithSingleChildAfterNodeWithManyChildren = node;
}
current = node;
}
return myShortPaths.get(key);
if (firstDirNodeWithSingleChildAfterNodeWithManyChildren == null) {
firstDirNodeWithSingleChildAfterNodeWithManyChildren = current;
}
final boolean skipDirs = firstDirNodeWithSingleChildAfterNodeWithManyChildren != firstDirNode;
StringBuilder b = new StringBuilder();
final Node firstCharacterOfDirectoryName = firstDirNodeWithSingleChildAfterNodeWithManyChildren != current || current.myChar==
INTERNAL_PATH_DELIMITER
? firstDirNodeWithSingleChildAfterNodeWithManyChildren.myParentNode // firstDirNodeWithSingleChildAfterNodeWithManyChildren.myChar == /
: firstDirNodeWithSingleChildAfterNodeWithManyChildren;
for(Node n = firstCharacterOfDirectoryName; n != myRootNode; ) {
if (n.myChar == INTERNAL_PATH_DELIMITER) b.append(mySeparator);
else b.append(n.myChar);
if (skipDirs && n.myChar == INTERNAL_PATH_DELIMITER && n != firstDirNode) {
// Skip intermediate path content which is the same till file name
n = n.myParentNode;
while(n != firstDirNode) n = n.myParentNode;
b.append("\u2026").append(mySeparator);
}
n = n.myParentNode;
}
return b.toString();
}
}