mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
406 lines
15 KiB
Java
406 lines
15 KiB
Java
/*
|
|
* Copyright 2000-2016 JetBrains s.r.o.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.intellij.ide.scratch;
|
|
|
|
import com.intellij.ide.FileIconProvider;
|
|
import com.intellij.ide.navigationToolbar.AbstractNavBarModelExtension;
|
|
import com.intellij.lang.Language;
|
|
import com.intellij.lang.LanguageUtil;
|
|
import com.intellij.lang.PerFileMappings;
|
|
import com.intellij.lang.PerFileMappingsBase;
|
|
import com.intellij.openapi.application.*;
|
|
import com.intellij.openapi.components.PersistentStateComponent;
|
|
import com.intellij.openapi.components.State;
|
|
import com.intellij.openapi.components.Storage;
|
|
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
|
import com.intellij.openapi.fileEditor.FileEditorManager;
|
|
import com.intellij.openapi.fileEditor.FileEditorManagerAdapter;
|
|
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
|
import com.intellij.openapi.fileEditor.impl.EditorTabTitleProvider;
|
|
import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl;
|
|
import com.intellij.openapi.fileEditor.impl.NonProjectFileWritingAccessExtension;
|
|
import com.intellij.openapi.fileTypes.*;
|
|
import com.intellij.openapi.module.Module;
|
|
import com.intellij.openapi.project.Project;
|
|
import com.intellij.openapi.project.ProjectManager;
|
|
import com.intellij.openapi.project.ProjectManagerAdapter;
|
|
import com.intellij.openapi.util.Iconable;
|
|
import com.intellij.openapi.util.io.FileUtil;
|
|
import com.intellij.openapi.util.text.StringUtil;
|
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
|
import com.intellij.openapi.vfs.VfsUtil;
|
|
import com.intellij.openapi.vfs.VirtualFile;
|
|
import com.intellij.psi.LanguageSubstitutor;
|
|
import com.intellij.psi.LanguageSubstitutors;
|
|
import com.intellij.psi.PsiElement;
|
|
import com.intellij.psi.search.*;
|
|
import com.intellij.psi.util.PsiUtilCore;
|
|
import com.intellij.usages.impl.rules.UsageType;
|
|
import com.intellij.usages.impl.rules.UsageTypeProvider;
|
|
import com.intellij.util.PathUtil;
|
|
import com.intellij.util.containers.ConcurrentFactoryMap;
|
|
import com.intellij.util.containers.ContainerUtil;
|
|
import com.intellij.util.indexing.IndexableSetContributor;
|
|
import com.intellij.util.indexing.LightDirectoryIndex;
|
|
import com.intellij.util.messages.MessageBus;
|
|
import org.jdom.Element;
|
|
import org.jetbrains.annotations.NotNull;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
import javax.swing.*;
|
|
import java.io.IOException;
|
|
import java.util.*;
|
|
|
|
|
|
@State(name = "ScratchFileService", storages = @Storage("scratches.xml"))
|
|
public class ScratchFileServiceImpl extends ScratchFileService implements PersistentStateComponent<Element>{
|
|
|
|
private static final RootType NULL_TYPE = new RootType("", null) {};
|
|
|
|
private final LightDirectoryIndex<RootType> myIndex;
|
|
private final MyLanguages myScratchMapping = new MyLanguages();
|
|
|
|
protected ScratchFileServiceImpl(Application application) {
|
|
myIndex = new LightDirectoryIndex<>(application, NULL_TYPE, index -> {
|
|
LocalFileSystem fileSystem = LocalFileSystem.getInstance();
|
|
for (RootType r : RootType.getAllRootIds()) {
|
|
index.putInfo(fileSystem.findFileByPath(getRootPath(r)), r);
|
|
}
|
|
});
|
|
initFileOpenedListener(application.getMessageBus());
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
public String getRootPath(@NotNull RootType rootId) {
|
|
return getRootPath() + "/" + rootId.getId();
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public RootType getRootType(@Nullable VirtualFile file) {
|
|
if (file == null) return null;
|
|
VirtualFile directory = file.isDirectory() ? file : file.getParent();
|
|
RootType result = myIndex.getInfoForFile(directory);
|
|
return result == NULL_TYPE ? null : result;
|
|
}
|
|
|
|
private void initFileOpenedListener(MessageBus messageBus) {
|
|
final FileEditorManagerAdapter editorListener = new FileEditorManagerAdapter() {
|
|
@Override
|
|
public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
|
|
if (!isEditable(file)) return;
|
|
RootType rootType = getRootType(file);
|
|
if (rootType == null) return;
|
|
rootType.fileOpened(file, source);
|
|
}
|
|
|
|
@Override
|
|
public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) {
|
|
if (Boolean.TRUE.equals(file.getUserData(FileEditorManagerImpl.CLOSING_TO_REOPEN))) return;
|
|
if (!isEditable(file)) return;
|
|
|
|
RootType rootType = getRootType(file);
|
|
if (rootType == null) return;
|
|
rootType.fileClosed(file, source);
|
|
}
|
|
|
|
boolean isEditable(@NotNull VirtualFile file) {
|
|
return FileDocumentManager.getInstance().getDocument(file) != null;
|
|
}
|
|
};
|
|
ProjectManagerAdapter projectListener = new ProjectManagerAdapter() {
|
|
@Override
|
|
public void projectOpened(Project project) {
|
|
project.getMessageBus().connect(project).subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, editorListener);
|
|
FileEditorManager editorManager = FileEditorManager.getInstance(project);
|
|
for (VirtualFile virtualFile : editorManager.getOpenFiles()) {
|
|
editorListener.fileOpened(editorManager, virtualFile);
|
|
}
|
|
}
|
|
};
|
|
for (Project project : ProjectManager.getInstance().getOpenProjects()) {
|
|
projectListener.projectOpened(project);
|
|
}
|
|
messageBus.connect().subscribe(ProjectManager.TOPIC, projectListener);
|
|
}
|
|
|
|
@NotNull
|
|
protected String getRootPath() {
|
|
return FileUtil.toSystemIndependentName(PathManager.getScratchPath());
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
public PerFileMappings<Language> getScratchesMapping() {
|
|
return myScratchMapping;
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public Element getState() {
|
|
return myScratchMapping.getState();
|
|
}
|
|
|
|
@Override
|
|
public void loadState(Element state) {
|
|
myScratchMapping.loadState(state);
|
|
}
|
|
|
|
private static class MyLanguages extends PerFileMappingsBase<Language> {
|
|
@Override
|
|
protected List<Language> getAvailableValues() {
|
|
return LanguageUtil.getFileLanguages();
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
protected String serialize(Language language) {
|
|
return language.getID();
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
protected Language handleUnknownMapping(VirtualFile file, String value) {
|
|
return PlainTextLanguage.INSTANCE;
|
|
}
|
|
}
|
|
|
|
public static class TypeFactory extends FileTypeFactory {
|
|
|
|
@Override
|
|
public void createFileTypes(@NotNull FileTypeConsumer consumer) {
|
|
consumer.consume(ScratchFileType.INSTANCE);
|
|
}
|
|
}
|
|
|
|
public static class Substitutor extends LanguageSubstitutor {
|
|
@Nullable
|
|
@Override
|
|
public Language getLanguage(@NotNull VirtualFile file, @NotNull Project project) {
|
|
return substituteLanguage(project, file);
|
|
}
|
|
|
|
@Nullable
|
|
public static Language substituteLanguage(@NotNull Project project, @NotNull VirtualFile file) {
|
|
RootType rootType = ScratchFileService.getInstance().getRootType(file);
|
|
if (rootType == null) return null;
|
|
Language language = rootType.substituteLanguage(project, file);
|
|
Language adjusted = language != null ? language : getLanguageByFileName(file);
|
|
return adjusted != null && adjusted != ScratchFileType.INSTANCE.getLanguage() ?
|
|
LanguageSubstitutors.INSTANCE.substituteLanguage(adjusted, file, project) : adjusted;
|
|
}
|
|
}
|
|
|
|
public static class Highlighter implements SyntaxHighlighterProvider {
|
|
@Override
|
|
@Nullable
|
|
public SyntaxHighlighter create(@NotNull FileType fileType, @Nullable Project project, @Nullable VirtualFile file) {
|
|
if (project == null || file == null || !(fileType instanceof ScratchFileType)) return null;
|
|
|
|
Language language = LanguageUtil.getLanguageForPsi(project, file);
|
|
return language == null ? null : SyntaxHighlighterFactory.getSyntaxHighlighter(language, project, file);
|
|
}
|
|
}
|
|
|
|
public static class FilePresentation implements FileIconProvider, EditorTabTitleProvider {
|
|
|
|
@Nullable
|
|
@Override
|
|
public Icon getIcon(@NotNull VirtualFile file, @Iconable.IconFlags int flags, @Nullable Project project) {
|
|
if (project == null || file.isDirectory()) return null;
|
|
RootType rootType = ScratchFileService.getInstance().getRootType(file);
|
|
if (rootType == null) return null;
|
|
return rootType.substituteIcon(project, file);
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public String getEditorTabTitle(@NotNull Project project, @NotNull VirtualFile file) {
|
|
RootType rootType = ScratchFileService.getInstance().getRootType(file);
|
|
if (rootType == null) return null;
|
|
return rootType.substituteName(project, file);
|
|
}
|
|
}
|
|
|
|
public static class AccessExtension implements NonProjectFileWritingAccessExtension {
|
|
|
|
@Override
|
|
public boolean isWritable(@NotNull VirtualFile file) {
|
|
return file.getFileType() == ScratchFileType.INSTANCE;
|
|
}
|
|
}
|
|
|
|
public static class NavBarExtension extends AbstractNavBarModelExtension {
|
|
|
|
@Nullable
|
|
@Override
|
|
public String getPresentableText(Object object) {
|
|
if (!(object instanceof PsiElement)) return null;
|
|
Project project = ((PsiElement)object).getProject();
|
|
VirtualFile virtualFile = PsiUtilCore.getVirtualFile((PsiElement)object);
|
|
if (virtualFile == null || !virtualFile.isValid()) return null;
|
|
RootType rootType = ScratchFileService.getInstance().getRootType(virtualFile);
|
|
if (rootType == null) return null;
|
|
if (virtualFile.isDirectory() && additionalRoots(project).contains(virtualFile)) {
|
|
return rootType.getDisplayName();
|
|
}
|
|
return rootType.substituteName(project, virtualFile);
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
public Collection<VirtualFile> additionalRoots(Project project) {
|
|
Set<VirtualFile> result = ContainerUtil.newLinkedHashSet();
|
|
LocalFileSystem fileSystem = LocalFileSystem.getInstance();
|
|
ScratchFileService app = ScratchFileService.getInstance();
|
|
for (RootType r : RootType.getAllRootIds()) {
|
|
ContainerUtil.addIfNotNull(result, fileSystem.findFileByPath(app.getRootPath(r)));
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public VirtualFile findFile(@NotNull RootType rootType, @NotNull String pathName, @NotNull Option option) throws IOException {
|
|
ApplicationManager.getApplication().assertReadAccessAllowed();
|
|
|
|
String fullPath = getRootPath(rootType) + "/" + pathName;
|
|
if (option != Option.create_new_always) {
|
|
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(fullPath);
|
|
if (file != null && !file.isDirectory()) return file;
|
|
if (option == Option.existing_only) return null;
|
|
}
|
|
String ext = PathUtil.getFileExtension(pathName);
|
|
String fileNameExt = PathUtil.getFileName(pathName);
|
|
String fileName = StringUtil.trimEnd(fileNameExt, ext == null ? "" : "." + ext);
|
|
return WriteAction.compute(() -> {
|
|
VirtualFile dir = VfsUtil.createDirectories(PathUtil.getParentPath(fullPath));
|
|
if (option == Option.create_new_always) {
|
|
return VfsUtil.createChildSequent(LocalFileSystem.getInstance(), dir, fileName, StringUtil.notNullize(ext));
|
|
}
|
|
else {
|
|
return dir.createChildData(LocalFileSystem.getInstance(), fileNameExt);
|
|
}
|
|
});
|
|
}
|
|
|
|
@Nullable
|
|
private static Language getLanguageByFileName(@Nullable VirtualFile file) {
|
|
return file == null ? null : LanguageUtil.getFileTypeLanguage(FileTypeManager.getInstance().getFileTypeByFileName(file.getName()));
|
|
}
|
|
|
|
@NotNull
|
|
public static GlobalSearchScope buildScratchesSearchScope() {
|
|
final ScratchFileService service = ScratchFileService.getInstance();
|
|
return new GlobalSearchScope() {
|
|
@NotNull
|
|
@Override
|
|
public String getDisplayName() {
|
|
return "Scratches and Consoles";
|
|
}
|
|
|
|
@Override
|
|
public boolean contains(@NotNull VirtualFile file) {
|
|
RootType rootType = file.getFileType() == ScratchFileType.INSTANCE ? service.getRootType(file) : null;
|
|
return rootType != null && !rootType.isHidden();
|
|
}
|
|
|
|
@Override
|
|
public boolean isSearchOutsideRootModel() {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSearchInModuleContent(@NotNull Module aModule) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean isSearchInLibraries() {
|
|
return false;
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
public GlobalSearchScope intersectWith(@NotNull GlobalSearchScope scope) {
|
|
if (scope instanceof ProjectAndLibrariesScope) return this;
|
|
return super.intersectWith(scope);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return getDisplayName();
|
|
}
|
|
};
|
|
}
|
|
|
|
public static class UseScopeExtension extends UseScopeEnlarger {
|
|
@Nullable
|
|
@Override
|
|
public SearchScope getAdditionalUseScope(@NotNull PsiElement element) {
|
|
SearchScope useScope = element.getUseScope();
|
|
if (useScope instanceof LocalSearchScope) return null;
|
|
return buildScratchesSearchScope();
|
|
}
|
|
}
|
|
|
|
public static class UsageTypeExtension implements UsageTypeProvider {
|
|
private static final ConcurrentFactoryMap<RootType, UsageType> ourUsageTypes = new ConcurrentFactoryMap<RootType, UsageType>() {
|
|
@Nullable
|
|
@Override
|
|
protected UsageType create(RootType key) {
|
|
return new UsageType("Usage in " + key.getDisplayName());
|
|
}
|
|
};
|
|
|
|
@Nullable
|
|
@Override
|
|
public UsageType getUsageType(PsiElement element) {
|
|
VirtualFile file = PsiUtilCore.getVirtualFile(element);
|
|
RootType rootType = file != null && file.getFileType() == ScratchFileType.INSTANCE ?
|
|
ScratchFileService.getInstance().getRootType(file) : null;
|
|
return rootType == null ? null : ourUsageTypes.get(rootType);
|
|
}
|
|
}
|
|
|
|
public static class IndexSetContributor extends IndexableSetContributor {
|
|
|
|
@NotNull
|
|
@Override
|
|
public Set<VirtualFile> getAdditionalRootsToIndex() {
|
|
ScratchFileService instance = ScratchFileService.getInstance();
|
|
LocalFileSystem fileSystem = LocalFileSystem.getInstance();
|
|
HashSet<VirtualFile> result = ContainerUtil.newHashSet();
|
|
for (RootType rootType : RootType.getAllRootIds()) {
|
|
if (rootType.isHidden()) continue;
|
|
ContainerUtil.addIfNotNull(result, fileSystem.findFileByPath(instance.getRootPath(rootType)));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@NotNull
|
|
@Override
|
|
public Set<VirtualFile> getAdditionalProjectRootsToIndex(@NotNull Project project) {
|
|
return Collections.emptySet();
|
|
}
|
|
}
|
|
} |