github: codereview
[idea/community.git] / platform / lang-impl / src / com / intellij / openapi / fileEditor / impl / UniqueVFilePathBuilderImpl.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.fileEditor.impl;
17
18 import com.intellij.ide.ui.UISettings;
19 import com.intellij.openapi.fileEditor.FileEditorManager;
20 import com.intellij.openapi.fileEditor.UniqueVFilePathBuilder;
21 import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
22 import com.intellij.openapi.project.Project;
23 import com.intellij.openapi.roots.ProjectRootManager;
24 import com.intellij.openapi.util.Key;
25 import com.intellij.openapi.util.io.FileUtil;
26 import com.intellij.openapi.util.io.UniqueNameBuilder;
27 import com.intellij.openapi.util.text.StringUtil;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.openapi.vfs.VirtualFilePathWrapper;
30 import com.intellij.psi.search.FilenameIndex;
31 import com.intellij.psi.search.GlobalSearchScope;
32 import com.intellij.psi.search.ProjectScope;
33 import com.intellij.psi.util.CachedValue;
34 import com.intellij.psi.util.CachedValueProvider;
35 import com.intellij.psi.util.CachedValuesManager;
36 import com.intellij.psi.util.PsiModificationTracker;
37 import com.intellij.util.containers.ConcurrentFactoryMap;
38 import gnu.trove.THashSet;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41
42 import java.io.File;
43 import java.util.Collection;
44 import java.util.Collections;
45
46 /**
47  * @author yole
48  */
49 public class UniqueVFilePathBuilderImpl extends UniqueVFilePathBuilder {
50   @NotNull
51   @Override
52   public String getUniqueVirtualFilePath(Project project, VirtualFile file) {
53     return getUniqueVirtualFilePath(project, file, false);
54   }
55
56   @NotNull
57   @Override
58   public String getUniqueVirtualFilePathWithinOpenedFileEditors(Project project, VirtualFile vFile) {
59     return getUniqueVirtualFilePath(project, vFile, true);
60   }
61
62   private static final Key<CachedValue<ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>>>
63     ourShortNameBuilderCacheKey = Key.create("project's.short.file.name.builder");
64   private static final Key<CachedValue<ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>>>
65     ourShortNameOpenedBuilderCacheKey = Key.create("project's.short.file.name.opened.builder");
66   private static final UniqueNameBuilder<VirtualFile> ourEmptyBuilder = new UniqueNameBuilder<VirtualFile>(null, null, -1);
67
68   private static String getUniqueVirtualFilePath(final Project project, VirtualFile file, final boolean skipNonOpenedFiles) {
69     Key<CachedValue<ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>>> key =
70       skipNonOpenedFiles ?  ourShortNameOpenedBuilderCacheKey:ourShortNameBuilderCacheKey;
71     CachedValue<ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>> data = project.getUserData(key);
72     if (data == null) {
73       project.putUserData(key, data = CachedValuesManager.getManager(project).createCachedValue(
74         new CachedValueProvider<ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>>() {
75           @Nullable
76           @Override
77           public Result<ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>> compute() {
78             return new Result<ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>>(
79               new ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>() {
80                 @Nullable
81                 @Override
82                 protected UniqueNameBuilder<VirtualFile> create(String key) {
83                   final UniqueNameBuilder<VirtualFile> builder = filesWithTheSameName(
84                     key,
85                     project,
86                     skipNonOpenedFiles,
87                     ProjectScope.getProjectScope(project)
88                   );
89                   return builder != null ? builder:ourEmptyBuilder;
90                 }
91               },
92               PsiModificationTracker.MODIFICATION_COUNT,
93               ProjectRootManager.getInstance(project),
94 //              VirtualFileManager.VFS_STRUCTURE_MODIFICATIONS,
95               FileEditorManagerImpl.OPEN_FILE_SET_MODIFICATION_COUNT
96             );
97           }
98         }, false));
99     }
100
101     UniqueNameBuilder<VirtualFile> uniqueNameBuilderForShortName = data.getValue().get(file.getName());
102     if (uniqueNameBuilderForShortName == ourEmptyBuilder) uniqueNameBuilderForShortName = null;
103
104     if (uniqueNameBuilderForShortName != null && uniqueNameBuilderForShortName.contains(file)) {
105       if (file instanceof VirtualFilePathWrapper) {
106         return ((VirtualFilePathWrapper)file).getPresentablePath();
107       }
108       return getEditorTabText(file, uniqueNameBuilderForShortName, UISettings.getInstance().HIDE_KNOWN_EXTENSION_IN_TABS);
109     }
110     return file.getPresentableName();
111   }
112
113   @Nullable
114   private static UniqueNameBuilder<VirtualFile> filesWithTheSameName(String fileName, Project project,
115                                                               boolean skipNonOpenedFiles,
116                                                               GlobalSearchScope scope) {
117     Collection<VirtualFile> filesWithSameName = skipNonOpenedFiles ? Collections.<VirtualFile>emptySet() :
118                                                 FilenameIndex.getVirtualFilesByName(project, fileName,
119                                                                                     scope);
120     THashSet<VirtualFile> setOfFilesWithTheSameName = new THashSet<VirtualFile>(filesWithSameName);
121     // add open files out of project scope
122     for(VirtualFile openFile: FileEditorManager.getInstance(project).getOpenFiles()) {
123       if (openFile.getName().equals(fileName)) {
124         setOfFilesWithTheSameName.add(openFile);
125       }
126     }
127     for (VirtualFile recentlyEditedFile : IdeDocumentHistory.getInstance(project).getChangedFiles()) {
128       if (recentlyEditedFile.getName().equals(fileName)) {
129         setOfFilesWithTheSameName.add(recentlyEditedFile);
130       }
131     }
132
133     filesWithSameName = setOfFilesWithTheSameName;
134
135     if (filesWithSameName.size() > 1) {
136       String path = project.getBasePath();
137       path = path == null ? "" : FileUtil.toSystemIndependentName(path);
138       UniqueNameBuilder<VirtualFile> builder = new UniqueNameBuilder<VirtualFile>(path, File.separator, 25);
139       for (VirtualFile virtualFile: filesWithSameName) {
140         builder.addPath(virtualFile, virtualFile.getPath());
141       }
142       return builder;
143     }
144     return null;
145   }
146
147   public static <T> String getEditorTabText(T key, UniqueNameBuilder<T> builder, boolean hideKnownExtensionInTabs) {
148     String result = builder.getShortPath(key);
149     if (hideKnownExtensionInTabs) {
150       String withoutExtension = FileUtil.getNameWithoutExtension(result);
151       if (StringUtil.isNotEmpty(withoutExtension) && !withoutExtension.endsWith(builder.getSeparator())) {
152         return withoutExtension;
153       }
154     }
155     return result;
156   }
157 }