mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
+21
-7
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user