mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
apply bidi layout algorithm only within lexical entities
This commit is contained in:
@@ -15,26 +15,43 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.view;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.LanguageParserDefinitions;
|
||||
import com.intellij.lang.ParserDefinition;
|
||||
import com.intellij.lexer.Lexer;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.FoldRegion;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.editor.VisualPosition;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import com.intellij.openapi.editor.colors.EditorFontType;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUIUtil;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterClient;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileTypes.LanguageFileType;
|
||||
import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.LanguageSubstitutors;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
/**
|
||||
* A facade for components responsible for drawing editor contents, managing editor size
|
||||
@@ -53,7 +70,8 @@ public class EditorView implements Disposable {
|
||||
private final EditorSizeManager mySizeManager;
|
||||
private final TextLayoutCache myTextLayoutCache;
|
||||
private final TabFragment myTabFragment;
|
||||
|
||||
|
||||
private EditorHighlighter myHighlighter; // accessed only in EDT
|
||||
private String myPrefixText; // accessed only in EDT
|
||||
private LineLayout myPrefixLayout; // guarded by myLock
|
||||
private TextAttributes myPrefixAttributes; // accessed only in EDT
|
||||
@@ -76,12 +94,72 @@ public class EditorView implements Disposable {
|
||||
mySizeManager = new EditorSizeManager(this);
|
||||
myTextLayoutCache = new TextLayoutCache(this);
|
||||
myTabFragment = new TabFragment(this);
|
||||
|
||||
Disposer.register(this, myTextLayoutCache);
|
||||
Disposer.register(this, mySizeManager);
|
||||
|
||||
reinitSettings();
|
||||
}
|
||||
|
||||
private void disposeHighlighter() {
|
||||
if (myHighlighter != null) {
|
||||
myDocument.removeDocumentListener(myHighlighter);
|
||||
myHighlighter = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateHighlighter() {
|
||||
disposeHighlighter();
|
||||
myHighlighter = createHighlighter(myEditor);
|
||||
if (myHighlighter != null) {
|
||||
myDocument.addDocumentListener(myHighlighter);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EditorHighlighter createHighlighter(Editor editor) {
|
||||
final Project project = editor.getProject();
|
||||
if (project == null) return null;
|
||||
final Document document = editor.getDocument();
|
||||
VirtualFile file = FileDocumentManager.getInstance().getFile(document);
|
||||
if (file == null || !(file.getFileType() instanceof LanguageFileType)) return null;
|
||||
Language draftLanguage = ((LanguageFileType)file.getFileType()).getLanguage();
|
||||
Language language = LanguageSubstitutors.INSTANCE.substituteLanguage(draftLanguage, file, project);
|
||||
ParserDefinition parserDefinition = LanguageParserDefinitions.INSTANCE.forLanguage(language);
|
||||
if (parserDefinition == null) return null;
|
||||
final Lexer lexer = parserDefinition.createLexer(project);
|
||||
LexerEditorHighlighter highlighter = new LexerEditorHighlighter(new SyntaxHighlighterBase() {
|
||||
@Override
|
||||
public
|
||||
@NotNull
|
||||
Lexer getHighlightingLexer() {
|
||||
return lexer;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {
|
||||
return EMPTY;
|
||||
}
|
||||
}, DUMMY_COLORS_SCHEME);
|
||||
highlighter.setEditor(new HighlighterClient() {
|
||||
@Override
|
||||
public Project getProject() {
|
||||
return project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repaint(int start, int end) {}
|
||||
|
||||
@Override
|
||||
public Document getDocument() {
|
||||
return document;
|
||||
}
|
||||
});
|
||||
highlighter.setText(document.getImmutableCharSequence());
|
||||
return highlighter;
|
||||
}
|
||||
|
||||
EditorImpl getEditor() {
|
||||
return myEditor;
|
||||
}
|
||||
@@ -102,8 +180,14 @@ public class EditorView implements Disposable {
|
||||
return myTabFragment;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
EditorHighlighter getHighlighter() {
|
||||
return myHighlighter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
disposeHighlighter();
|
||||
}
|
||||
|
||||
public int yToVisualLine(int y) {
|
||||
@@ -240,6 +324,7 @@ public class EditorView implements Disposable {
|
||||
reset();
|
||||
setPrefix(myPrefixText, myPrefixAttributes); // recreate prefix layout
|
||||
invalidateFoldRegionLayouts();
|
||||
updateHighlighter();
|
||||
}
|
||||
|
||||
public void invalidateRange(int startOffset, int endOffset) {
|
||||
@@ -397,4 +482,14 @@ public class EditorView implements Disposable {
|
||||
private static void assertIsReadAccess() {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
}
|
||||
|
||||
private static final EditorColorsScheme DUMMY_COLORS_SCHEME = (EditorColorsScheme)
|
||||
Proxy.newProxyInstance(EditorView.class.getClassLoader(),
|
||||
new Class[]{EditorColorsScheme.class},
|
||||
new InvocationHandler() {
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
package com.intellij.openapi.editor.impl.view;
|
||||
|
||||
import com.intellij.openapi.editor.colors.FontPreferences;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
|
||||
import com.intellij.openapi.editor.impl.ComplementaryFontsRegistry;
|
||||
import com.intellij.openapi.editor.impl.EditorImpl;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.intellij.lang.annotations.JdkConstants;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -80,7 +83,7 @@ class LineLayout {
|
||||
EditorImpl editor = view.getEditor();
|
||||
FontPreferences fontPreferences = editor.getColorsScheme().getFontPreferences();
|
||||
char[] chars = CharArrayUtil.fromSequence(editor.getDocument().getImmutableCharSequence(), lineStartOffset, lineEndOffset);
|
||||
List<BidiRun> runs = createRuns(editor, chars);
|
||||
List<BidiRun> runs = createRuns(view, chars, lineStartOffset);
|
||||
for (BidiRun run : runs) {
|
||||
IterationState it = new IterationState(editor, lineStartOffset + run.startOffset, lineStartOffset + run.endOffset,
|
||||
false, false, false, false);
|
||||
@@ -100,7 +103,7 @@ class LineLayout {
|
||||
EditorImpl editor = view.getEditor();
|
||||
FontPreferences fontPreferences = editor.getColorsScheme().getFontPreferences();
|
||||
char[] chars = CharArrayUtil.fromSequence(text);
|
||||
List<BidiRun> runs = createRuns(editor, chars);
|
||||
List<BidiRun> runs = createRuns(view, chars, -1);
|
||||
for (BidiRun run : runs) {
|
||||
addFragments(run, chars, run.startOffset, run.endOffset, fontStyle, fontPreferences, fontRenderContext, null);
|
||||
assert !run.fragments.isEmpty();
|
||||
@@ -108,17 +111,56 @@ class LineLayout {
|
||||
return runs;
|
||||
}
|
||||
|
||||
private static List<BidiRun> createRuns(EditorImpl editor, char[] text) {
|
||||
if (editor.myDisableRtl) return Collections.singletonList(new BidiRun((byte)0, 0, text.length));
|
||||
Bidi bidi = new Bidi(text, 0, null, 0, text.length, Bidi.DIRECTION_LEFT_TO_RIGHT);
|
||||
int runCount = bidi.getRunCount();
|
||||
List<BidiRun> runs = new ArrayList<BidiRun>(runCount);
|
||||
for (int i = 0; i < runCount; i++) {
|
||||
runs.add(new BidiRun((byte)bidi.getRunLevel(i), bidi.getRunStart(i), bidi.getRunLimit(i)));
|
||||
private static List<BidiRun> createRuns(EditorView view, char[] text, int startOffsetInEditor) {
|
||||
int textLength = text.length;
|
||||
if (view.getEditor().myDisableRtl) return Collections.singletonList(new BidiRun((byte)0, 0, textLength));
|
||||
List<BidiRun> runs = new ArrayList<BidiRun>();
|
||||
EditorHighlighter highlighter = view.getHighlighter();
|
||||
if (startOffsetInEditor >= 0 && highlighter != null) {
|
||||
// running bidi algorithm separately for text fragments corresponding to different lexer tokens
|
||||
int lastOffset = startOffsetInEditor;
|
||||
IElementType lastToken = null;
|
||||
HighlighterIterator iterator = highlighter.createIterator(startOffsetInEditor);
|
||||
int endOffsetInEditor = startOffsetInEditor + textLength;
|
||||
while (!iterator.atEnd() && iterator.getStart() < endOffsetInEditor) {
|
||||
IElementType currentToken = iterator.getTokenType();
|
||||
if (currentToken != lastToken) {
|
||||
int tokenStart = Math.max(iterator.getStart(), startOffsetInEditor);
|
||||
addRuns(runs, text, lastOffset - startOffsetInEditor, tokenStart - startOffsetInEditor);
|
||||
lastToken = currentToken;
|
||||
lastOffset = tokenStart;
|
||||
}
|
||||
iterator.advance();
|
||||
}
|
||||
addRuns(runs, text, lastOffset - startOffsetInEditor, endOffsetInEditor - startOffsetInEditor);
|
||||
}
|
||||
else {
|
||||
addRuns(runs, text, 0, textLength);
|
||||
}
|
||||
return runs;
|
||||
}
|
||||
|
||||
private static void addRuns(List<BidiRun> runs, char[] text, int start, int end) {
|
||||
if (start >= end) return;
|
||||
Bidi bidi = new Bidi(text, start, null, 0, end - start, Bidi.DIRECTION_LEFT_TO_RIGHT);
|
||||
int runCount = bidi.getRunCount();
|
||||
for (int i = 0; i < runCount; i++) {
|
||||
addOrMergeRun(runs, new BidiRun((byte)bidi.getRunLevel(i), start + bidi.getRunStart(i), start + bidi.getRunLimit(i)));
|
||||
}
|
||||
}
|
||||
|
||||
private static void addOrMergeRun(List<BidiRun> runs, BidiRun run) {
|
||||
int size = runs.size();
|
||||
if (size > 0 && runs.get(size - 1).level == 0 && run.level == 0) {
|
||||
BidiRun lastRun = runs.remove(size - 1);
|
||||
assert lastRun.endOffset == run.startOffset;
|
||||
runs.add(new BidiRun((byte)0, lastRun.startOffset, run.endOffset));
|
||||
}
|
||||
else {
|
||||
runs.add(run);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addFragments(BidiRun run, char[] text, int start, int end, int fontStyle,
|
||||
FontPreferences fontPreferences, FontRenderContext fontRenderContext,
|
||||
@Nullable TabFragment tabFragment) {
|
||||
|
||||
Reference in New Issue
Block a user