Fixes IDEA-218532 Freezes in CodeStyle.getSettings

Code style settings are computed asynchronously on a background thread with a notification sent upon completion.

GitOrigin-RevId: 8e82a1e540f1880961788703e42f7f1e21ad62a4
This commit is contained in:
Rustam Vishnyakov
2019-08-06 18:06:34 +03:00
committed by intellij-monorepo-bot
parent a191c2eae3
commit 08210fb4d2
6 changed files with 160 additions and 80 deletions
@@ -11,6 +11,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.*;
import com.intellij.psi.codeStyle.modifier.CodeStyleSettingsModifier;
import com.intellij.psi.codeStyle.modifier.TransientCodeStyleSettings;
import com.intellij.psi.util.CachedValuesManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
@@ -70,6 +71,12 @@ public class CodeStyle {
*/
@NotNull
public static CodeStyleSettings getSettings(@NotNull PsiFile file) {
final Project project = file.getProject();
CodeStyleSettings tempSettings = CodeStyleSettingsManager.getInstance(project).getTemporarySettings();
if (tempSettings != null) {
return tempSettings;
}
for (FileCodeStyleProvider provider : FileCodeStyleProvider.EP_NAME.getIterable()) {
CodeStyleSettings fileSettings = provider.getSettings(file);
if (fileSettings != null) {
@@ -78,9 +85,9 @@ public class CodeStyle {
}
if (!file.isPhysical()) {
return getSettings(file.getProject());
return getSettings(project);
}
return CodeStyleCachingUtil.getCachedCodeStyle(file);
return CachedValuesManager.getCachedValue(file, CodeStyleCachedValueProvider.getInstance(file));
}
@@ -0,0 +1,149 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.application.options;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SimpleModificationTracker;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.modifier.CodeStyleSettingsModifier;
import com.intellij.psi.codeStyle.modifier.TransientCodeStyleSettings;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
class CodeStyleCachedValueProvider implements CachedValueProvider<CodeStyleSettings> {
private final static Logger LOG = Logger.getInstance(CodeStyleCachedValueProvider.class);
private final static Key<CodeStyleCachedValueProvider> PROVIDER_KEY = Key.create("code.style.cached.value.provider");
private final @NotNull PsiFile myFile;
private final @NotNull AsyncComputation myComputation;
CodeStyleCachedValueProvider(@NotNull PsiFile file) {
myFile = file;
myComputation = new AsyncComputation();
}
@NotNull
@Override
public Result<CodeStyleSettings> compute() {
CodeStyleSettings settings = myComputation.getCurrResult();
logCached(myFile, settings);
return new Result<>(settings, getDependencies(settings, myComputation));
}
@NotNull
Object[] getDependencies(@NotNull CodeStyleSettings settings, @NotNull AsyncComputation computation) {
List<Object> dependencies = new ArrayList<>();
if (settings instanceof TransientCodeStyleSettings) {
dependencies.addAll(((TransientCodeStyleSettings)settings).getDependencies());
}
else {
dependencies.add(settings.getModificationTracker());
}
dependencies.add(computation.getTracker());
return ArrayUtil.toObjectArray(dependencies);
}
static synchronized CodeStyleCachedValueProvider getInstance(@NotNull PsiFile file) {
CodeStyleCachedValueProvider instance = file.getUserData(PROVIDER_KEY);
if (instance == null) {
instance = new CodeStyleCachedValueProvider(file);
file.putUserData(PROVIDER_KEY, instance);
}
return instance;
}
private void notifyCachedValueComputed(@NotNull PsiFile file) {
final Application application = ApplicationManager.getApplication();
if (!application.isUnitTestMode()) {
application.invokeLater(() -> {
final CodeStyleSettingsManager settingsManager = CodeStyleSettingsManager.getInstance(file.getProject());
settingsManager.fireCodeStyleSettingsChanged(file);
myComputation.reset();
});
}
}
private static void logCached(@NotNull PsiFile file, @NotNull CodeStyleSettings settings) {
LOG.debug(String.format(
"File: %s (%s), cached: %s, tracker: %d", file.getName(), Integer.toHexString(file.hashCode()), settings,
settings.getModificationTracker().getModificationCount()));
}
/**
* Always contains some result which can be obtained by {@code getCurrResult()} method. Listeners are notified after
* the computation is finished and {@code getCurrResult()} contains a stable computed value.
*/
private class AsyncComputation {
private final AtomicBoolean myIsActive = new AtomicBoolean();
private volatile @NotNull CodeStyleSettings myCurrResult;
private final @NotNull CodeStyleSettingsManager mySettingsManager;
private final SimpleModificationTracker myTracker = new SimpleModificationTracker();
private AsyncComputation() {
mySettingsManager = CodeStyleSettingsManager.getInstance(myFile.getProject());
//noinspection deprecation
myCurrResult = mySettingsManager.getCurrentSettings();
}
private void start() {
final Application application = ApplicationManager.getApplication();
if (!application.isUnitTestMode()) {
application.executeOnPooledThread(() -> computeSettings());
}
else {
computeSettings();
}
}
private synchronized void computeSettings() {
if (LOG.isDebugEnabled()) {
LOG.debug("Computation started for " + myFile.getName());
}
@SuppressWarnings("deprecation")
CodeStyleSettings currSettings = mySettingsManager.getCurrentSettings();
if (currSettings != mySettingsManager.getTemporarySettings()) {
TransientCodeStyleSettings modifiableSettings = new TransientCodeStyleSettings(myFile, currSettings);
for (CodeStyleSettingsModifier modifier : CodeStyleSettingsModifier.EP_NAME.getExtensionList()) {
if (modifier.modifySettings(modifiableSettings, myFile)) {
LOG.debug("Modifier: " + modifier.getClass().getName());
modifiableSettings.setModifier(modifier);
currSettings = modifiableSettings;
break;
}
}
}
myCurrResult = currSettings;
myTracker.incModificationCount();
if (LOG.isDebugEnabled()) {
LOG.debug("Computation ended for " + myFile.getName());
}
notifyCachedValueComputed(myFile);
}
@NotNull
public CodeStyleSettings getCurrResult() {
if (myIsActive.compareAndSet(false, true)) {
start();
}
return myCurrResult;
}
private SimpleModificationTracker getTracker() {
return myTracker;
}
void reset() {
myIsActive.set(false);
}
}
}
@@ -1,75 +0,0 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.application.options;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.modifier.CodeStyleSettingsModifier;
import com.intellij.psi.codeStyle.modifier.TransientCodeStyleSettings;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import org.jetbrains.annotations.NotNull;
class CodeStyleCachingUtil {
private final static Logger LOG = Logger.getInstance(CodeStyleCachingUtil.class);
@NotNull
static CodeStyleSettings getCachedCodeStyle(@NotNull PsiFile file) {
CachedCodeStyleHolder cachedCodeStyleHolder = CachedValuesManager.getCachedValue(file, () -> createHolder(file).getCachedResult());
return cachedCodeStyleHolder.getCachedSettings();
}
private static CachedCodeStyleHolder createHolder(@NotNull PsiFile file) {
CachedCodeStyleHolder holder = new CachedCodeStyleHolder();
holder.compute(file);
if (LOG.isDebugEnabled()) {
logCached(file, holder);
}
return holder;
}
static class CachedCodeStyleHolder {
private @NotNull CodeStyleSettings myCachedSettings = CodeStyle.getDefaultSettings();
private void compute(@NotNull PsiFile file) {
final CodeStyleSettingsManager settingsManager = CodeStyleSettingsManager.getInstance(file.getProject());
@SuppressWarnings("deprecation")
CodeStyleSettings currSettings = myCachedSettings = settingsManager.getCurrentSettings();
if (currSettings != settingsManager.getTemporarySettings()) {
TransientCodeStyleSettings modifiableSettings = new TransientCodeStyleSettings(file, currSettings);
for (CodeStyleSettingsModifier modifier : CodeStyleSettingsModifier.EP_NAME.getExtensionList()) {
if (modifier.modifySettings(modifiableSettings, file)) {
LOG.debug("Modifier: " + modifier.getClass().getName());
modifiableSettings.setModifier(modifier);
currSettings = modifiableSettings;
break;
}
}
}
myCachedSettings = currSettings;
}
@NotNull
Object[] getDependencies() {
return myCachedSettings instanceof TransientCodeStyleSettings ?
((TransientCodeStyleSettings)myCachedSettings).getDependencies().toArray() :
new Object[]{myCachedSettings.getModificationTracker()};
}
@NotNull
CodeStyleSettings getCachedSettings() {
return myCachedSettings;
}
CachedValueProvider.Result<CachedCodeStyleHolder> getCachedResult() {
return new CachedValueProvider.Result<>(this, this.getDependencies());
}
}
private static void logCached(@NotNull PsiFile file, @NotNull CachedCodeStyleHolder holder) {
CodeStyleSettings settings = holder.getCachedSettings();
LOG.debug(String.format(
"File: %s (%s), cached: %s, tracker: %d", file.getName(), Integer.toHexString(file.hashCode()), settings, settings.getModificationTracker().getModificationCount()));
}
}
@@ -148,12 +148,10 @@ public class CodeStyleSettingsManager implements PersistentStateComponent<Elemen
* @see #dropTemporarySettings()
*/
public void setTemporarySettings(@NotNull CodeStyleSettings settings) {
updateSettingsTracker();
myTemporarySettings = settings;
}
public void dropTemporarySettings() {
updateSettingsTracker();
myTemporarySettings = null;
}
@@ -60,6 +60,7 @@ public class FileCodeStyleProviderTest extends UsefulTestCase {
public void testFileCodeStyleProvider() {
PsiFile file = myFixture.configureByText("a.java", "class Foo {}");
CodeStyle.dropTemporarySettings(file.getProject());
CodeStyleSettings settings = CodeStyle.getSettings(file);
assertSame(myTestSettings, settings);
}
@@ -59,7 +59,7 @@ class EditorConfigFileHierarchyServiceImpl(
private fun updateHandlers(project: Project) {
updateQueue.queue(Update.create("editorconfig hierarchy update") {
CodeStyleSettingsManager.getInstance(project).fireCodeStyleSettingsChanged(null)
CodeStyleSettingsManager.getInstance(project).notifyCodeStyleSettingsChanged()
})
}