From c35e30ee22c23f9cab6ab2e98242d2f25ff5f7e8 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 6 Dec 2012 09:04:07 +0100 Subject: [PATCH] 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" --- .../fileEditor/UniqueNameBuilderTest.java | 28 ++- .../openapi/util/io/UniqueNameBuilder.java | 199 +++++++----------- 2 files changed, 94 insertions(+), 133 deletions(-) diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/UniqueNameBuilderTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/UniqueNameBuilderTest.java index 5435f9ed2603..816454d5422d 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/UniqueNameBuilderTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/fileEditor/UniqueNameBuilderTest.java @@ -18,30 +18,36 @@ public class UniqueNameBuilderTest extends TestCase { UniqueNameBuilder builder = new UniqueNameBuilder("", "/", 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 builder = new UniqueNameBuilder("", "\\", 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 builder = new UniqueNameBuilder("/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("", "/", 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 builder = new UniqueNameBuilder("/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 builder = new UniqueNameBuilder("/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")); } } diff --git a/platform/util/src/com/intellij/openapi/util/io/UniqueNameBuilder.java b/platform/util/src/com/intellij/openapi/util/io/UniqueNameBuilder.java index ee2632e12db3..6697b4fa5681 100644 --- a/platform/util/src/com/intellij/openapi/util/io/UniqueNameBuilder.java +++ b/platform/util/src/com/intellij/openapi/util/io/UniqueNameBuilder.java @@ -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 { - private final Map myPaths = new HashMap(); - private final Map myShortPaths = new HashMap(); - private int myAbbreviateLevel = 1; - private final String myRoot; + public static final char INTERNAL_PATH_DELIMITER = '/'; + private final Map myPaths = new THashMap(); 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 { myMaxLength = maxLength; } + private static class Node { + final char myChar; + final TIntObjectHashMap myChildren; + final Node myParentNode; + + Node(char ch, Node parentNode) { + myChar = ch; + myParentNode = parentNode; + myChildren = new TIntObjectHashMap(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> segmentLists = new HashMap>(); - for (Map.Entry entry : myPaths.entrySet()) { - List 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> entry : segmentLists.entrySet()) { - final List 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 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> segmentLists) { - for(int i=1; i> segmentLists, int index) { - Map truncatedSegments = ContainerUtil.newHashMap(); - for (Map.Entry> 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 inverted = ContainerUtil.newHashMap(); - for (Map.Entry entry : truncatedSegments.entrySet()) { - if (entry.getValue() != NON_UNIQUE) { - inverted.put(entry.getValue(), entry.getKey()); - } - } - for (Map.Entry> 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 splitIntoSegments(String path) { - int pos = path.lastIndexOf('/'); - if (pos < 0) return Collections.singletonList(path); - List segments = new ArrayList(); - 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(); } }