2 * Copyright 2000-2014 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.openapi.fileEditor.impl;
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;
43 import java.util.Collection;
44 import java.util.Collections;
49 public class UniqueVFilePathBuilderImpl extends UniqueVFilePathBuilder {
52 public String getUniqueVirtualFilePath(Project project, VirtualFile file) {
53 return getUniqueVirtualFilePath(project, file, false);
58 public String getUniqueVirtualFilePathWithinOpenedFileEditors(Project project, VirtualFile vFile) {
59 return getUniqueVirtualFilePath(project, vFile, true);
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);
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);
73 project.putUserData(key, data = CachedValuesManager.getManager(project).createCachedValue(
74 new CachedValueProvider<ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>>() {
77 public Result<ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>> compute() {
78 return new Result<ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>>(
79 new ConcurrentFactoryMap<String, UniqueNameBuilder<VirtualFile>>() {
82 protected UniqueNameBuilder<VirtualFile> create(String key) {
83 final UniqueNameBuilder<VirtualFile> builder = filesWithTheSameName(
87 ProjectScope.getProjectScope(project)
89 return builder != null ? builder:ourEmptyBuilder;
92 PsiModificationTracker.MODIFICATION_COUNT,
93 ProjectRootManager.getInstance(project),
94 // VirtualFileManager.VFS_STRUCTURE_MODIFICATIONS,
95 FileEditorManagerImpl.OPEN_FILE_SET_MODIFICATION_COUNT
101 UniqueNameBuilder<VirtualFile> uniqueNameBuilderForShortName = data.getValue().get(file.getName());
102 if (uniqueNameBuilderForShortName == ourEmptyBuilder) uniqueNameBuilderForShortName = null;
104 if (uniqueNameBuilderForShortName != null && uniqueNameBuilderForShortName.contains(file)) {
105 if (file instanceof VirtualFilePathWrapper) {
106 return ((VirtualFilePathWrapper)file).getPresentablePath();
108 return getEditorTabText(file, uniqueNameBuilderForShortName, UISettings.getInstance().HIDE_KNOWN_EXTENSION_IN_TABS);
110 return file.getPresentableName();
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,
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);
127 for (VirtualFile recentlyEditedFile : IdeDocumentHistory.getInstance(project).getChangedFiles()) {
128 if (recentlyEditedFile.getName().equals(fileName)) {
129 setOfFilesWithTheSameName.add(recentlyEditedFile);
133 filesWithSameName = setOfFilesWithTheSameName;
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());
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;