IDEA-68357 IDEA unresponsive with large MXML files

Suppress injected text repaint requests triggered by injected-unaware lexer-based highlighter
This commit is contained in:
Denis Zhdanov
2011-06-20 12:40:53 +04:00
parent 5ec316d268
commit f44dbd1983
5 changed files with 140 additions and 6 deletions
@@ -0,0 +1,74 @@
/*
* Copyright 2000-2011 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.editor.ex;
import com.intellij.injected.editor.DocumentWindow;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.impl.EditorRepaintStrategy;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* This strategy handles the situation when typing is performed inside parsed injected text. Usually editor's highlighter
* doesn't know anything about injected context internals and sees it just as a single big token (e.g. MXML files may contain
* Flash code inside CDATA comment and the highlighter (based on XML lexer) considers the whole Flash code block to be just
* a CDATA token).
* <p/>
* So, every time user types at injected context, highlighter receives document change event and asks editor to repaint
* affected token. That is rather heavy operation if performed frequently for the large text range. That's why current
* strategy handles such requests to the whole injected context repaint and skips them.
* <p/>
* It's assumed that corresponding repainting is performed during editor's markup model updates triggered by injected
* context processing.
*
* @author Denis Zhdanov
* @since 6/17/11 11:16 AM
*/
public class InjectedAwareEditorRepaintStrategy implements EditorRepaintStrategy {
@Override
public TextRange adjustHighlighterRegion(@NotNull EditorEx editor, int startOffset, int endOffset) {
return isInjectedContext(editor, startOffset, endOffset) ? null : new TextRange(startOffset, endOffset);
}
private static boolean isInjectedContext(@NotNull EditorEx editor, int startOffset, int endOffset) {
final Project project = editor.getProject();
if (project == null) {
return false;
}
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
final PsiFile psiFile = documentManager.getCachedPsiFile(editor.getDocument());
if (psiFile == null) {
return false;
}
final List<DocumentWindow> injectedDocuments = InjectedLanguageUtil.getCachedInjectedDocuments(psiFile);
for (DocumentWindow injectedDocument : injectedDocuments) {
for (RangeMarker rangeMarker : injectedDocument.getHostRanges()) {
if (Math.max(rangeMarker.getStartOffset(), startOffset) <= Math.min(rangeMarker.getEndOffset(), endOffset)) {
return true;
}
}
}
return false;
}
}
@@ -54,6 +54,7 @@ import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces;
import com.intellij.openapi.editor.impl.softwrap.SoftWrapDrawingType;
import com.intellij.openapi.editor.impl.softwrap.SoftWrapHelper;
import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
import com.intellij.openapi.ide.CopyPasteManager;
@@ -1312,22 +1313,33 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return line * getLineHeight();
}
public void repaint(int startOffset, int endOffset) {
public void repaint(final int startOffset, final int endOffset) {
if (!isShowing() || myScrollPane == null || myDocument.isInBulkUpdate()) {
return;
}
int startOffsetToUse = startOffset;
int endOffsetToUse = endOffset;
assertIsDispatchThread();
if (endOffset > myDocument.getTextLength()) {
endOffset = myDocument.getTextLength();
if (endOffsetToUse > myDocument.getTextLength()) {
endOffsetToUse = myDocument.getTextLength();
}
for (EditorRepaintStrategy repaintStrategy : Extensions.getExtensions(EditorRepaintStrategy.EP_NAME)) {
final TextRange range = repaintStrategy.adjustHighlighterRegion(this, startOffsetToUse, endOffsetToUse);
if (range == null) {
return;
}
startOffsetToUse = range.getStartOffset();
endOffsetToUse = range.getEndOffset();
}
// We do repaint in case of equal offsets because there is a possible case that there is a soft wrap at the same offset and
// it does occupy particular amount of visual space that may be necessary to repaint.
if (startOffset <= endOffset) {
int startLine = myDocument.getLineNumber(startOffset);
int endLine = myDocument.getLineNumber(endOffset);
if (startOffsetToUse <= endOffsetToUse) {
int startLine = myDocument.getLineNumber(startOffsetToUse);
int endLine = myDocument.getLineNumber(endOffsetToUse);
repaintLines(startLine, endLine);
}
}
@@ -0,0 +1,45 @@
/*
* Copyright 2000-2011 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.editor.impl;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Defines contract for the strategy that may adjust/control editor's repainting.
*
* @author Denis Zhdanov
* @since 6/17/11 11:04 AM
*/
public interface EditorRepaintStrategy {
ExtensionPointName<EditorRepaintStrategy> EP_NAME = ExtensionPointName.create("com.intellij.editorRepaintStrategy");
/**
* Asks current strategy to adjust (if necessary) target document region repaint request received from the given editor's highlighter.
*
* @param editor target editor
* @param startOffset start offset of the document text range requested to be repainted (inclusive)
* @param endOffset end offset of the document text range requested to be repainted (exclusive)
* @return actual text range to repaint (if any); <code>null</code> as an indication that no further processing
* for the current repaint request should be performed
*/
@Nullable
TextRange adjustHighlighterRegion(@NotNull EditorEx editor, int startOffset, int endOffset);
}
@@ -456,6 +456,7 @@
<codeFoldingOptionsProvider instance="com.intellij.application.options.editor.BaseCodeFoldingOptionsProvider" order="first"/>
<editorOptionsProvider instance="com.intellij.application.options.editor.EditorSmartKeysConfigurable"/>
<editorOptionsProvider instance="com.intellij.application.options.editor.EditorAppearanceConfigurable"/>
<editorRepaintStrategy implementation="com.intellij.openapi.editor.ex.InjectedAwareEditorRepaintStrategy"/>
<editorCustomization implementation="com.intellij.ui.SoftWrapsEditorCustomization"/>
<editorCustomization implementation="com.intellij.ui.HorizontalScrollBarEditorCustomization"/>
<editorCustomization implementation="com.intellij.ui.AdditionalPageAtBottomEditorCustomization"/>
@@ -112,6 +112,8 @@
<extensionPoint name="editorCustomization" area="IDEA_PROJECT" interface="com.intellij.ui.EditorCustomization"/>
<extensionPoint name="editorNavigation" interface="com.intellij.openapi.editor.EditorNavigationDelegate"/>
<extensionPoint name="editorRepaintStrategy" interface="com.intellij.openapi.editor.impl.EditorRepaintStrategy"/>
<extensionPoint name="statistics.usagesCollector" interface="com.intellij.internal.statistic.UsagesCollector"/>
<extensionPoint name="xmlRpcHandler" beanClass="com.intellij.ide.XmlRpcHandlerBean"/>