blob: 6dcceff534098d712e02fdbdbd91155b4f50af28 [file] [log] [blame]
/*
* Copyright 2000-2014 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.
*/
/*
* @author max
*/
package com.intellij.openapi.fileEditor.impl.text;
import com.intellij.codeHighlighting.BackgroundEditorHighlighter;
import com.intellij.codeInsight.daemon.impl.TextEditorBackgroundHighlighter;
import com.intellij.codeInsight.folding.CodeFoldingManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.util.Producer;
import com.intellij.util.ui.UIUtil;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
public class PsiAwareTextEditorProvider extends TextEditorProvider implements AsyncFileEditorProvider {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorProvider");
@NonNls
private static final String FOLDING_ELEMENT = "folding";
@Override
@NotNull
public FileEditor createEditor(@NotNull final Project project, @NotNull final VirtualFile file) {
return createEditorAsync(project, file).build();
}
@NotNull
@Override
public Builder createEditorAsync(@NotNull final Project project, @NotNull final VirtualFile file) {
if (!accept(project, file)) {
LOG.error("Cannot open text editor for " + file);
}
CodeFoldingState state = null;
try {
Document document = FileDocumentManager.getInstance().getDocument(file);
if (document != null) {
state = CodeFoldingManager.getInstance(project).buildInitialFoldings(document);
}
}
catch (Exception e) {
LOG.error("Error building initial foldings", e);
}
final CodeFoldingState finalState = state;
return new Builder() {
@Override
public FileEditor build() {
final PsiAwareTextEditorImpl editor = new PsiAwareTextEditorImpl(project, file, PsiAwareTextEditorProvider.this);
if (finalState != null) {
finalState.setToEditor(editor.getEditor());
}
return editor;
}
};
}
@Override
@NotNull
public FileEditorState readState(@NotNull final Element element, @NotNull final Project project, @NotNull final VirtualFile file) {
final TextEditorState state = (TextEditorState)super.readState(element, project, file);
// Foldings
Element child = element.getChild(FOLDING_ELEMENT);
Document document = FileDocumentManager.getInstance().getCachedDocument(file);
if (child != null) {
if (document == null) {
final Element detachedStateCopy = child.clone();
state.setDelayedFoldState(new Producer<CodeFoldingState>() {
@Override
public CodeFoldingState produce() {
Document document = FileDocumentManager.getInstance().getCachedDocument(file);
return document == null ? null : CodeFoldingManager.getInstance(project).readFoldingState(detachedStateCopy, document);
}
});
}
else {
//PsiDocumentManager.getInstance(project).commitDocument(document);
state.setFoldingState(CodeFoldingManager.getInstance(project).readFoldingState(child, document));
}
}
return state;
}
@Override
public void writeState(@NotNull final FileEditorState _state, @NotNull final Project project, @NotNull final Element element) {
super.writeState(_state, project, element);
TextEditorState state = (TextEditorState)_state;
// Foldings
CodeFoldingState foldingState = state.getFoldingState();
if (foldingState != null) {
Element e = new Element(FOLDING_ELEMENT);
try {
CodeFoldingManager.getInstance(project).writeFoldingState(foldingState, e);
}
catch (WriteExternalException e1) {
//ignore
}
element.addContent(e);
}
}
@Override
protected TextEditorState getStateImpl(final Project project, @NotNull final Editor editor, @NotNull final FileEditorStateLevel level) {
final TextEditorState state = super.getStateImpl(project, editor, level);
// Save folding only on FULL level. It's very expensive to commit document on every
// type (caused by undo).
if(FileEditorStateLevel.FULL == level){
// Folding
if (project != null && !project.isDisposed() && !editor.isDisposed() && project.isInitialized()) {
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
state.setFoldingState(CodeFoldingManager.getInstance(project).saveFoldingState(editor));
}
else {
state.setFoldingState(null);
}
}
return state;
}
@Override
protected void setStateImpl(final Project project, final Editor editor, final TextEditorState state) {
super.setStateImpl(project, editor, state);
// Folding
final CodeFoldingState foldState = state.getFoldingState();
if (project != null && foldState != null){
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
editor.getFoldingModel().runBatchFoldingOperation(
new Runnable() {
@Override
public void run() {
CodeFoldingManager.getInstance(project).restoreFoldingState(editor, foldState);
}
}
);
}
});
}
}
@NotNull
@Override
protected EditorWrapper createWrapperForEditor(@NotNull final Editor editor) {
return new PsiAwareEditorWrapper(editor);
}
private final class PsiAwareEditorWrapper extends EditorWrapper {
private final TextEditorBackgroundHighlighter myBackgroundHighlighter;
private PsiAwareEditorWrapper(@NotNull Editor editor) {
super(editor);
final Project project = editor.getProject();
myBackgroundHighlighter = project == null
? null
: new TextEditorBackgroundHighlighter(project, editor);
}
@Override
public BackgroundEditorHighlighter getBackgroundHighlighter() {
return myBackgroundHighlighter;
}
}
}