mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-80001 remember recent files, that were deleted and restored later
This commit is contained in:
+53
-33
@@ -29,6 +29,7 @@ import com.intellij.openapi.startup.StartupManager;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
@@ -45,7 +46,7 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
@State(name = "editorHistoryManager", storages = @Storage(file = StoragePathMacros.WORKSPACE_FILE))
|
||||
public final class EditorHistoryManager implements PersistentStateComponent<Element> {
|
||||
public final class EditorHistoryManager implements PersistentStateComponent<Element>, ProjectComponent {
|
||||
private static final Logger LOG = Logger.getInstance(EditorHistoryManager.class);
|
||||
|
||||
private final Project myProject;
|
||||
@@ -84,7 +85,13 @@ public final class EditorHistoryManager implements PersistentStateComponent<Elem
|
||||
}
|
||||
|
||||
private synchronized void removeEntry(HistoryEntry entry) {
|
||||
boolean removed = myEntriesList.remove(entry);
|
||||
if (removed) entry.destroy();
|
||||
}
|
||||
|
||||
private synchronized void moveOnTop(HistoryEntry entry) {
|
||||
myEntriesList.remove(entry);
|
||||
myEntriesList.add(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,8 +129,7 @@ public final class EditorHistoryManager implements PersistentStateComponent<Elem
|
||||
|
||||
final HistoryEntry entry = getEntry(file);
|
||||
if(entry != null){
|
||||
removeEntry(entry);
|
||||
addEntry(entry);
|
||||
moveOnTop(entry);
|
||||
}
|
||||
else {
|
||||
final FileEditorState[] states=new FileEditorState[editors.length];
|
||||
@@ -134,7 +140,7 @@ public final class EditorHistoryManager implements PersistentStateComponent<Elem
|
||||
providers[i] = provider;
|
||||
states[i] = editors[i].getState(FileEditorStateLevel.FULL);
|
||||
}
|
||||
addEntry(new HistoryEntry(file, providers, states, providers[selectedProviderIndex]));
|
||||
addEntry(HistoryEntry.createHeavy(myProject, file, providers, states, providers[selectedProviderIndex]));
|
||||
trimToSize();
|
||||
}
|
||||
}
|
||||
@@ -195,24 +201,11 @@ public final class EditorHistoryManager implements PersistentStateComponent<Elem
|
||||
final Pair <FileEditor, FileEditorProvider> selectedEditorWithProvider = editorManager.getSelectedEditorWithProvider(file);
|
||||
if (selectedEditorWithProvider != null) {
|
||||
//LOG.assertTrue(selectedEditorWithProvider != null);
|
||||
entry.mySelectedProvider = selectedEditorWithProvider.getSecond ();
|
||||
LOG.assertTrue(entry.mySelectedProvider != null);
|
||||
entry.setSelectedProvider(selectedEditorWithProvider.getSecond());
|
||||
LOG.assertTrue(entry.getSelectedProvider() != null);
|
||||
|
||||
if(changeEntryOrderOnly){
|
||||
removeEntry(entry);
|
||||
addEntry(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all entries that correspond to invalid files
|
||||
*/
|
||||
private synchronized void validateEntries(){
|
||||
for(int i=myEntriesList.size()-1; i>=0; i--){
|
||||
final HistoryEntry entry = myEntriesList.get(i);
|
||||
if(!entry.myFile.isValid()){
|
||||
myEntriesList.remove(i);
|
||||
moveOnTop(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -221,12 +214,12 @@ public final class EditorHistoryManager implements PersistentStateComponent<Elem
|
||||
* @return array of valid files that are in the history, oldest first. May contain duplicates.
|
||||
*/
|
||||
public synchronized VirtualFile[] getFiles(){
|
||||
validateEntries();
|
||||
final VirtualFile[] result = new VirtualFile[myEntriesList.size()];
|
||||
for(int i=myEntriesList.size()-1; i>=0 ;i--){
|
||||
result[i] = myEntriesList.get(i).myFile;
|
||||
final List<VirtualFile> result = new ArrayList<VirtualFile>(myEntriesList.size());
|
||||
for (HistoryEntry entry : myEntriesList) {
|
||||
VirtualFile file = entry.getFile();
|
||||
if (file != null) result.add(file);
|
||||
}
|
||||
return result;
|
||||
return VfsUtilCore.toVirtualFileArray(result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,7 +228,7 @@ public final class EditorHistoryManager implements PersistentStateComponent<Elem
|
||||
public LinkedHashSet<VirtualFile> getFileSet() {
|
||||
LinkedHashSet<VirtualFile> result = ContainerUtil.newLinkedHashSet();
|
||||
for (VirtualFile file : getFiles()) {
|
||||
// if the file occurs several times in the history, only its last occurrence counts
|
||||
// if the file occurs several times in the history, only its last occurrence counts
|
||||
result.remove(file);
|
||||
result.add(file);
|
||||
}
|
||||
@@ -244,7 +237,7 @@ public final class EditorHistoryManager implements PersistentStateComponent<Elem
|
||||
|
||||
public synchronized boolean hasBeenOpen(@NotNull VirtualFile f) {
|
||||
for (HistoryEntry each : myEntriesList) {
|
||||
if (Comparing.equal(each.myFile, f)) return true;
|
||||
if (Comparing.equal(each.getFile(), f)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -273,14 +266,14 @@ public final class EditorHistoryManager implements PersistentStateComponent<Elem
|
||||
*/
|
||||
public FileEditorProvider getSelectedProvider(final VirtualFile file) {
|
||||
final HistoryEntry entry = getEntry(file);
|
||||
return entry != null ? entry.mySelectedProvider : null;
|
||||
return entry != null ? entry.getSelectedProvider() : null;
|
||||
}
|
||||
|
||||
private synchronized HistoryEntry getEntry(@NotNull VirtualFile file){
|
||||
validateEntries();
|
||||
private synchronized HistoryEntry getEntry(@NotNull VirtualFile file) {
|
||||
for (int i = myEntriesList.size() - 1; i >= 0; i--) {
|
||||
final HistoryEntry entry = myEntriesList.get(i);
|
||||
if(file.equals(entry.myFile)){
|
||||
VirtualFile entryFile = entry.getFile();
|
||||
if (file.equals(entryFile)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
@@ -294,7 +287,8 @@ public final class EditorHistoryManager implements PersistentStateComponent<Elem
|
||||
private synchronized void trimToSize(){
|
||||
final int limit = UISettings.getInstance().RECENT_FILES_LIMIT + 1;
|
||||
while(myEntriesList.size()>limit){
|
||||
myEntriesList.remove(0);
|
||||
HistoryEntry removed = myEntriesList.remove(0);
|
||||
removed.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +303,7 @@ public final class EditorHistoryManager implements PersistentStateComponent<Elem
|
||||
public void run() {
|
||||
for (Element e : state.getChildren(HistoryEntry.TAG)) {
|
||||
try {
|
||||
addEntry(new HistoryEntry(myProject, e));
|
||||
addEntry(HistoryEntry.createHeavy(myProject, e));
|
||||
}
|
||||
catch (InvalidDataException e1) {
|
||||
// OK here
|
||||
@@ -345,6 +339,32 @@ public final class EditorHistoryManager implements PersistentStateComponent<Elem
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void projectOpened() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void projectClosed() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initComponent() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void disposeComponent() {
|
||||
for (HistoryEntry entry : myEntriesList) {
|
||||
entry.destroy();
|
||||
}
|
||||
myEntriesList.clear();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getComponentName() {
|
||||
return "editorHistoryManager";
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates history
|
||||
*/
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ public class EditorWithProviderComposite extends EditorComposite {
|
||||
final int selectedProviderIndex = ArrayUtil.find(editors, getSelectedEditor());
|
||||
LOG.assertTrue(selectedProviderIndex != -1);
|
||||
final FileEditorProvider[] providers = getProviders();
|
||||
return new HistoryEntry(getFile(), providers, states, providers[selectedProviderIndex]);
|
||||
return HistoryEntry.createLight(getFile(), providers, states, providers[selectedProviderIndex]);
|
||||
}
|
||||
|
||||
public void addEditor(@NotNull FileEditor editor, FileEditorProvider provider) {
|
||||
|
||||
+5
-4
@@ -862,19 +862,20 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener
|
||||
try {
|
||||
final FileEditorManagerImpl fileEditorManager = getManager();
|
||||
Element historyElement = file.getChild(HistoryEntry.TAG);
|
||||
final VirtualFile virtualFile = HistoryEntry.getVirtualFile(historyElement);
|
||||
final HistoryEntry entry = HistoryEntry.createLight(fileEditorManager.getProject(), historyElement);
|
||||
final VirtualFile virtualFile = entry.getFile();
|
||||
if (virtualFile == null) throw new InvalidDataException("No file exists: " + entry.getFilePointer().getUrl());
|
||||
Document document = ApplicationManager.getApplication().runReadAction(new Computable<Document>() {
|
||||
@Override
|
||||
public Document compute() {
|
||||
return virtualFile.isValid() ? FileDocumentManager.getInstance().getDocument(virtualFile) : null;
|
||||
}
|
||||
});
|
||||
final HistoryEntry entry = new HistoryEntry(fileEditorManager.getProject(), historyElement);
|
||||
final boolean isCurrentInTab = Boolean.valueOf(file.getAttributeValue(CURRENT_IN_TAB)).booleanValue();
|
||||
Boolean pin = Boolean.valueOf(file.getAttributeValue(PINNED));
|
||||
fileEditorManager.openFileImpl4(window, entry.myFile, entry, isCurrentInTab, isCurrentInTab, pin, i);
|
||||
fileEditorManager.openFileImpl4(window, virtualFile, entry, isCurrentInTab, isCurrentInTab, pin, i);
|
||||
if (isCurrentInTab) {
|
||||
focusedFile = entry.myFile;
|
||||
focusedFile = virtualFile;
|
||||
}
|
||||
if (document != null) {
|
||||
// This is just to make sure document reference is kept on stack till this point
|
||||
|
||||
+1
-1
@@ -915,7 +915,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec
|
||||
.getSelectedFileEditorProvider(myEditorHistoryManager, file, providers);
|
||||
}
|
||||
else {
|
||||
selectedProvider = entry.mySelectedProvider;
|
||||
selectedProvider = entry.getSelectedProvider();
|
||||
}
|
||||
if (selectedProvider != null) {
|
||||
for (int i = editors.length - 1; i >= 0; i--) {
|
||||
|
||||
+151
-41
@@ -15,21 +15,33 @@
|
||||
*/
|
||||
package com.intellij.openapi.fileEditor.impl;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.fileEditor.FileEditorProvider;
|
||||
import com.intellij.openapi.fileEditor.FileEditorState;
|
||||
import com.intellij.openapi.fileEditor.ex.FileEditorProviderManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ui.LightFilePointer;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.InvalidDataException;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
|
||||
import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
final class HistoryEntry{
|
||||
/**
|
||||
* `Heavy` entries should be disposed with {@link #destroy()} to prevent leak of VirtualFilePointer
|
||||
*/
|
||||
final class HistoryEntry {
|
||||
@NonNls static final String TAG = "entry";
|
||||
private static final String FILE_ATTR = "file";
|
||||
@NonNls private static final String PROVIDER_ELEMENT = "provider";
|
||||
@@ -37,43 +49,92 @@ final class HistoryEntry{
|
||||
@NonNls private static final String SELECTED_ATTR_VALUE = "selected";
|
||||
@NonNls private static final String STATE_ELEMENT = "state";
|
||||
|
||||
public final VirtualFile myFile;
|
||||
@NotNull private final VirtualFilePointer myFilePointer;
|
||||
/**
|
||||
* can be null when read from XML
|
||||
*/
|
||||
public FileEditorProvider mySelectedProvider;
|
||||
private final HashMap<FileEditorProvider, FileEditorState> myProvider2State;
|
||||
*/
|
||||
@Nullable private FileEditorProvider mySelectedProvider;
|
||||
@NotNull private final HashMap<FileEditorProvider, FileEditorState> myProvider2State;
|
||||
|
||||
public HistoryEntry(@NotNull VirtualFile file, @NotNull FileEditorProvider[] providers, @NotNull FileEditorState[] states, @NotNull FileEditorProvider selectedProvider){
|
||||
myFile = file;
|
||||
myProvider2State = new HashMap<FileEditorProvider, FileEditorState>();
|
||||
@Nullable private final Disposable myDisposable;
|
||||
|
||||
private HistoryEntry(@NotNull VirtualFilePointer filePointer,
|
||||
@Nullable FileEditorProvider selectedProvider,
|
||||
@Nullable Disposable disposable) {
|
||||
myFilePointer = filePointer;
|
||||
mySelectedProvider = selectedProvider;
|
||||
for (int i = 0; i < providers.length; i++) {
|
||||
putState(providers[i], states[i]);
|
||||
}
|
||||
myDisposable = disposable;
|
||||
myProvider2State = new HashMap<FileEditorProvider, FileEditorState>();
|
||||
}
|
||||
|
||||
public HistoryEntry(@NotNull Project project, @NotNull Element e) throws InvalidDataException {
|
||||
myFile = getVirtualFile(e);
|
||||
myProvider2State = new HashMap<FileEditorProvider, FileEditorState>();
|
||||
|
||||
for (Element _e : e.getChildren(PROVIDER_ELEMENT)) {
|
||||
String typeId = _e.getAttributeValue(EDITOR_TYPE_ID_ATTR);
|
||||
FileEditorProvider provider = FileEditorProviderManager.getInstance().getProvider(typeId);
|
||||
if (provider == null) {
|
||||
continue;
|
||||
}
|
||||
if (Boolean.valueOf(_e.getAttributeValue(SELECTED_ATTR_VALUE))) {
|
||||
mySelectedProvider = provider;
|
||||
}
|
||||
|
||||
Element stateElement = _e.getChild(STATE_ELEMENT);
|
||||
if (stateElement == null) {
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
putState(provider, provider.readState(stateElement, project, myFile));
|
||||
@NotNull
|
||||
public static HistoryEntry createLight(@NotNull VirtualFile file,
|
||||
@NotNull FileEditorProvider[] providers,
|
||||
@NotNull FileEditorState[] states,
|
||||
@NotNull FileEditorProvider selectedProvider) {
|
||||
VirtualFilePointer pointer = new LightFilePointer(file);
|
||||
HistoryEntry entry = new HistoryEntry(pointer, selectedProvider, null);
|
||||
for (int i = 0; i < providers.length; i++) {
|
||||
entry.putState(providers[i], states[i]);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static HistoryEntry createLight(@NotNull Project project, @NotNull Element e) throws InvalidDataException {
|
||||
EntryData entryData = parseEntry(project, e);
|
||||
|
||||
VirtualFilePointer pointer = new LightFilePointer(entryData.url);
|
||||
HistoryEntry entry = new HistoryEntry(pointer, entryData.selectedProvider, null);
|
||||
for (Pair<FileEditorProvider, FileEditorState> state : entryData.providerStates) {
|
||||
entry.putState(state.first, state.second);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static HistoryEntry createHeavy(@NotNull Project project,
|
||||
@NotNull VirtualFile file,
|
||||
@NotNull FileEditorProvider[] providers,
|
||||
@NotNull FileEditorState[] states,
|
||||
@NotNull FileEditorProvider selectedProvider) {
|
||||
if (project.isDisposed()) return createLight(file, providers, states, selectedProvider);
|
||||
|
||||
Disposable disposable = Disposer.newDisposable();
|
||||
VirtualFilePointer pointer = VirtualFilePointerManager.getInstance().create(file, disposable, null);
|
||||
|
||||
HistoryEntry entry = new HistoryEntry(pointer, selectedProvider, disposable);
|
||||
for (int i = 0; i < providers.length; i++) {
|
||||
entry.putState(providers[i], states[i]);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static HistoryEntry createHeavy(@NotNull Project project, @NotNull Element e) throws InvalidDataException {
|
||||
if (project.isDisposed()) return createLight(project, e);
|
||||
|
||||
EntryData entryData = parseEntry(project, e);
|
||||
|
||||
Disposable disposable = Disposer.newDisposable();
|
||||
VirtualFilePointer pointer = VirtualFilePointerManager.getInstance().create(entryData.url, disposable, null);
|
||||
|
||||
HistoryEntry entry = new HistoryEntry(pointer, entryData.selectedProvider, disposable);
|
||||
for (Pair<FileEditorProvider, FileEditorState> state : entryData.providerStates) {
|
||||
entry.putState(state.first, state.second);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public VirtualFilePointer getFilePointer() {
|
||||
return myFilePointer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public VirtualFile getFile() {
|
||||
return myFilePointer.getFile();
|
||||
}
|
||||
|
||||
public FileEditorState getState(@NotNull FileEditorProvider provider) {
|
||||
@@ -84,6 +145,19 @@ final class HistoryEntry{
|
||||
myProvider2State.put(provider, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FileEditorProvider getSelectedProvider() {
|
||||
return mySelectedProvider;
|
||||
}
|
||||
|
||||
public void setSelectedProvider(@Nullable FileEditorProvider value) {
|
||||
mySelectedProvider = value;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
if (myDisposable != null) Disposer.dispose(myDisposable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return element that was added to the <code>element</code>.
|
||||
* Returned element has tag {@link #TAG}. Never null.
|
||||
@@ -91,7 +165,7 @@ final class HistoryEntry{
|
||||
public Element writeExternal(Element element, Project project) {
|
||||
Element e = new Element(TAG);
|
||||
element.addContent(e);
|
||||
e.setAttribute(FILE_ATTR, myFile.getUrl());
|
||||
e.setAttribute(FILE_ATTR, myFilePointer.getUrl());
|
||||
|
||||
for (final Map.Entry<FileEditorProvider, FileEditorState> entry : myProvider2State.entrySet()) {
|
||||
FileEditorProvider provider = entry.getKey();
|
||||
@@ -112,16 +186,52 @@ final class HistoryEntry{
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static VirtualFile getVirtualFile(Element historyElement) throws InvalidDataException {
|
||||
if (!historyElement.getName().equals(TAG)) {
|
||||
throw new IllegalArgumentException("unexpected tag: " + historyElement);
|
||||
private static EntryData parseEntry(@NotNull Project project, @NotNull Element e) throws InvalidDataException {
|
||||
if (!e.getName().equals(TAG)) {
|
||||
throw new IllegalArgumentException("unexpected tag: " + e);
|
||||
}
|
||||
|
||||
String url = historyElement.getAttributeValue(FILE_ATTR);
|
||||
VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url);
|
||||
if (file == null){
|
||||
throw new InvalidDataException("No file exists: " + url);
|
||||
String url = e.getAttributeValue(FILE_ATTR);
|
||||
List<Pair<FileEditorProvider, FileEditorState>> providerStates = new ArrayList<Pair<FileEditorProvider, FileEditorState>>();
|
||||
FileEditorProvider selectedProvider = null;
|
||||
|
||||
VirtualFile file = LocalFileSystem.getInstance().findFileByPath(url);
|
||||
|
||||
for (Element _e : e.getChildren(PROVIDER_ELEMENT)) {
|
||||
String typeId = _e.getAttributeValue(EDITOR_TYPE_ID_ATTR);
|
||||
FileEditorProvider provider = FileEditorProviderManager.getInstance().getProvider(typeId);
|
||||
if (provider == null) {
|
||||
continue;
|
||||
}
|
||||
if (Boolean.valueOf(_e.getAttributeValue(SELECTED_ATTR_VALUE))) {
|
||||
selectedProvider = provider;
|
||||
}
|
||||
|
||||
Element stateElement = _e.getChild(STATE_ELEMENT);
|
||||
if (stateElement == null) {
|
||||
throw new InvalidDataException();
|
||||
}
|
||||
|
||||
if (file != null) {
|
||||
FileEditorState state = provider.readState(stateElement, project, file);
|
||||
providerStates.add(Pair.create(provider, state));
|
||||
}
|
||||
}
|
||||
|
||||
return new EntryData(url, providerStates, selectedProvider);
|
||||
}
|
||||
|
||||
private static class EntryData {
|
||||
@NotNull public final String url;
|
||||
@NotNull public final List<Pair<FileEditorProvider, FileEditorState>> providerStates;
|
||||
@Nullable public final FileEditorProvider selectedProvider;
|
||||
|
||||
public EntryData(@NotNull String url,
|
||||
@NotNull List<Pair<FileEditorProvider, FileEditorState>> providerStates,
|
||||
@Nullable FileEditorProvider selectedProvider) {
|
||||
this.url = url;
|
||||
this.providerStates = providerStates;
|
||||
this.selectedProvider = selectedProvider;
|
||||
}
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user