CPP-10639 Saving folding state triggers lazy blocks reparsing from EDT

Cancels storing of folding state by timeout on EDT. The problem comes from
smart pointers, that initiate lazy block parsing. It's is hard in C++.
Here is temporal solution util the platform start collecting the folding
state in background.

(cherry picked from commit edf15a5)
This commit is contained in:
Alexey Utkin
2017-11-03 18:58:58 +03:00
parent f487d41be1
commit b8bfcd38db
3 changed files with 113 additions and 32 deletions
@@ -30,9 +30,11 @@ import com.intellij.openapi.editor.FoldRegion;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.impl.text.CodeFoldingState;
import com.intellij.openapi.progress.util.ProgressWithTimeoutInDispatch;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtil;
@@ -204,7 +206,7 @@ class DocumentFoldingInfo implements JDOMExternalizable, CodeFoldingState {
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
public void writeExternal(final Element element) throws WriteExternalException {
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
if (myPsiElements.isEmpty() && myRangeMarkers.isEmpty() && mySerializedElements.isEmpty()){
@@ -212,39 +214,42 @@ class DocumentFoldingInfo implements JDOMExternalizable, CodeFoldingState {
}
if (mySerializedElements.isEmpty()) {
for (SmartPsiElementPointer<PsiElement> ptr : myPsiElements) {
PsiElement psiElement = ptr.getElement();
if (psiElement == null || !psiElement.isValid()) {
continue;
}
FoldingInfo fi = psiElement.getUserData(FOLDING_INFO_KEY);
boolean state = fi != null && fi.expanded;
String signature = FoldingPolicy.getSignature(psiElement);
if (signature == null) {
continue;
}
ProgressWithTimeoutInDispatch.execInDispatchWithTimeout(() -> {
for (SmartPsiElementPointer<PsiElement> ptr : myPsiElements) {
PsiElement psiElement = ptr.getElement();
if (psiElement == null || !psiElement.isValid()) {
continue;
}
FoldingInfo fi = psiElement.getUserData(FOLDING_INFO_KEY);
boolean state = fi != null && fi.expanded;
String signature = FoldingPolicy.getSignature(psiElement);
if (signature == null) {
continue;
}
PsiFile containingFile = psiElement.getContainingFile();
PsiElement restoredElement = FoldingPolicy.restoreBySignature(containingFile, signature);
if (!psiElement.equals(restoredElement)) {
StringBuilder trace = new StringBuilder();
PsiElement restoredAgain = FoldingPolicy.restoreBySignature(containingFile, signature, trace);
LOG.error("element: " + psiElement + "(" + psiElement.getText()
+ "); restoredElement: " + restoredElement
+ "; signature: '" + signature
+ "'; file: " + containingFile
+ "; injected: " + InjectedLanguageManager.getInstance(myProject).isInjectedFragment(containingFile)
+ "; languages: " + containingFile.getViewProvider().getLanguages()
+ "; restored again: " + restoredAgain +
"; restore produces same results: " + (restoredAgain == restoredElement)
+ "; trace:\n" + trace);
}
PsiFile containingFile = psiElement.getContainingFile();
PsiElement restoredElement = FoldingPolicy.restoreBySignature(containingFile, signature);
if (!psiElement.equals(restoredElement)) {
StringBuilder trace = new StringBuilder();
PsiElement restoredAgain = FoldingPolicy.restoreBySignature(containingFile, signature, trace);
LOG.error("element: " + psiElement + "(" + psiElement.getText()
+ "); restoredElement: " + restoredElement
+ "; signature: '" + signature
+ "'; file: " + containingFile
+ "; injected: " + InjectedLanguageManager.getInstance(myProject).isInjectedFragment(containingFile)
+ "; languages: " + containingFile.getViewProvider().getLanguages()
+ "; restored again: " + restoredAgain +
"; restore produces same results: " + (restoredAgain == restoredElement)
+ "; trace:\n" + trace);
}
Element e = new Element(ELEMENT_TAG);
e.setAttribute(SIGNATURE_ATT, signature);
e.setAttribute(EXPANDED_ATT, Boolean.toString(state));
element.addContent(e);
}
Element e = new Element(ELEMENT_TAG);
e.setAttribute(SIGNATURE_ATT, signature);
e.setAttribute(EXPANDED_ATT, Boolean.toString(state));
element.addContent(e);
}
return Void.TYPE;
}, Registry.get("save.folding.state.timeout").asInteger());
}
else {
// get back postponed state (before folding initialization)
@@ -0,0 +1,74 @@
/*
* Copyright 2000-2017 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.openapi.progress.util;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Computable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ProgressWithTimeoutInDispatch extends AbstractProgressIndicatorExBase implements PingProgress {
private final long myTimeoutMs;
private final long myStartTimeMs;
private ProgressWithTimeoutInDispatch(long timeoutMs) {
super();
myTimeoutMs = timeoutMs;
myStartTimeMs = System.currentTimeMillis();
setIndeterminate(true);
}
@Override
public boolean isCanceled() {
if (System.currentTimeMillis() - myStartTimeMs >= myTimeoutMs) {
cancel();
}
return super.isCanceled();
}
@Override
public void interact() {}
/**
* Cancels the <code>action</code> by the <code>ProcessCanceledException</code> exception
* after <code>timeoutMs</code> ms on event dispatch thread.
*
* The <code>action</code> is not limited by any timeout if it is called on non-dispatch
* thread, or <code>timeoutMs <= 0</code>, or the thread has progress indicator.
*
* @throws ProcessCanceledException
*/
@Nullable
public static <T> T execInDispatchWithTimeout(@NotNull Computable<T> action, long timeoutMs) throws ProcessCanceledException {
final Application application = ApplicationManager.getApplication();
if (application.isDispatchThread()
&& timeoutMs > 0
&& ProgressManager.getInstance().getProgressIndicator() == null) {
try {
return ProgressManager.getInstance().runProcess(action, new ProgressWithTimeoutInDispatch(timeoutMs));
}
catch (ProcessCanceledException ex) {
return null;
}
}
return action.compute();
}
}
@@ -1080,6 +1080,8 @@ welcome.screen.project.grouping.enabled.description=Allows to group recent proje
custom.folding.max.lookup.depth=50
custom.folding.max.lookup.depth.description=Max lookup depth for custom folding comments in a PSI tree.
save.folding.state.timeout=0
save.folding.state.timeout.description=Timeout after which folding state saving is forcibly cancelled (0 means no time limit)
project.structure.add.tools.jar.to.new.jdk=false
project.structure.add.tools.jar.to.new.jdk.description=Automatically add tools.jar to 'Classpath' when new JDK is created