From d3bf00a8c243e2f9baa5978c97fd1deb27772125 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Thu, 14 Jul 2016 19:38:13 +0300 Subject: [PATCH] IDEA-158519 Setting to override default text direction for mixed LTR/RTL text --- .../SetEditorBidiTextDirectionAction.java | 61 +++++++++++++++++++ .../openapi/editor/ex/BidiTextDirection.java | 20 ++++++ .../ex/EditorSettingsExternalizable.java | 10 +++ .../openapi/editor/impl/view/EditorView.java | 17 ++++++ .../openapi/editor/impl/view/LineLayout.java | 34 +++++------ .../src/messages/ActionsBundle.properties | 6 ++ .../src/idea/PlatformActions.xml | 8 +++ 7 files changed, 138 insertions(+), 18 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/openapi/editor/actions/SetEditorBidiTextDirectionAction.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/editor/ex/BidiTextDirection.java diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/SetEditorBidiTextDirectionAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/SetEditorBidiTextDirectionAction.java new file mode 100644 index 000000000000..b2a42132dfef --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/SetEditorBidiTextDirectionAction.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2016 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.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; +import com.intellij.openapi.editor.ex.BidiTextDirection; + +public abstract class SetEditorBidiTextDirectionAction extends ToggleAction { + private final BidiTextDirection myDirection; + + private SetEditorBidiTextDirectionAction(BidiTextDirection direction) { + myDirection = direction; + } + + @Override + public boolean isSelected(AnActionEvent e) { + return EditorSettingsExternalizable.getInstance().getBidiTextDirection() == myDirection; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + if (myDirection != EditorSettingsExternalizable.getInstance().getBidiTextDirection()) { + EditorSettingsExternalizable.getInstance().setBidiTextDirection(myDirection); + EditorFactory.getInstance().refreshAllEditors(); + } + } + + public static class ContentBased extends SetEditorBidiTextDirectionAction { + public ContentBased() { + super(BidiTextDirection.CONTENT_BASED); + } + } + + public static class Ltr extends SetEditorBidiTextDirectionAction { + public Ltr() { + super(BidiTextDirection.LTR); + } + } + + public static class Rtl extends SetEditorBidiTextDirectionAction { + public Rtl() { + super(BidiTextDirection.RTL); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/BidiTextDirection.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/BidiTextDirection.java new file mode 100644 index 000000000000..4a4173bae91c --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/BidiTextDirection.java @@ -0,0 +1,20 @@ +/* + * Copyright 2000-2016 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; + +public enum BidiTextDirection { + CONTENT_BASED, LTR, RTL +} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java index 566c3f5d2f2b..b362f96a9ef5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/EditorSettingsExternalizable.java @@ -84,6 +84,8 @@ public class EditorSettingsExternalizable implements PersistentStateComponent createFragments(@NotNull EditorView view, int line, boolean skipBidiLayout) { - EditorImpl editor = view.getEditor(); - Document document = editor.getDocument(); + Document document = view.getEditor().getDocument(); int lineStartOffset = document.getLineStartOffset(line); int lineEndOffset = document.getLineEndOffset(line); if (lineEndOffset <= lineStartOffset) return Collections.emptyList(); if (skipBidiLayout) return Collections.singletonList(new BidiRun(lineEndOffset - lineStartOffset)); CharSequence text = document.getImmutableCharSequence().subSequence(lineStartOffset, lineEndOffset); char[] chars = CharArrayUtil.fromSequence(text); - return createRuns(editor, chars, lineStartOffset); + return createRuns(view, chars, lineStartOffset); } private static List createFragments(@NotNull EditorView view, @NotNull CharSequence text, @JdkConstants.FontStyle int fontStyle) { if (text.length() == 0) return Collections.emptyList(); - EditorImpl editor = view.getEditor(); FontRenderContext fontRenderContext = view.getFontRenderContext(); - FontPreferences fontPreferences = editor.getColorsScheme().getFontPreferences(); + FontPreferences fontPreferences = view.getEditor().getColorsScheme().getFontPreferences(); char[] chars = CharArrayUtil.fromSequence(text); - List runs = createRuns(editor, chars, -1); + List runs = createRuns(view, chars, -1); for (BidiRun run : runs) { for (Chunk chunk : run.getChunks()) { chunk.fragments = new ArrayList<>(); @@ -123,32 +120,33 @@ abstract class LineLayout { return runs; } - private static List createRuns(EditorImpl editor, char[] text, int startOffsetInEditor) { + private static List createRuns(EditorView view, char[] text, int startOffsetInEditor) { int textLength = text.length; - if (editor.myDisableRtl || !Bidi.requiresBidi(text, 0, textLength)) { + if (view.getEditor().myDisableRtl || !Bidi.requiresBidi(text, 0, textLength)) { return Collections.singletonList(new BidiRun(textLength)); } List runs = new ArrayList(); + int flags = view.getBidiFlags(); if (startOffsetInEditor >= 0) { // running bidi algorithm separately for text fragments corresponding to different lexer tokens int lastOffset = startOffsetInEditor; IElementType lastToken = null; - HighlighterIterator iterator = editor.getHighlighter().createIterator(startOffsetInEditor); + HighlighterIterator iterator = view.getEditor().getHighlighter().createIterator(startOffsetInEditor); int endOffsetInEditor = startOffsetInEditor + textLength; while (!iterator.atEnd() && iterator.getStart() < endOffsetInEditor) { IElementType currentToken = iterator.getTokenType(); if (distinctTokens(lastToken, currentToken)) { int tokenStart = Math.max(iterator.getStart(), startOffsetInEditor); - addRuns(runs, text, lastOffset - startOffsetInEditor, tokenStart - startOffsetInEditor); + addRuns(runs, text, lastOffset - startOffsetInEditor, tokenStart - startOffsetInEditor, flags); lastToken = currentToken; lastOffset = tokenStart; } iterator.advance(); } - addRuns(runs, text, lastOffset - startOffsetInEditor, endOffsetInEditor - startOffsetInEditor); + addRuns(runs, text, lastOffset - startOffsetInEditor, endOffsetInEditor - startOffsetInEditor, flags); } else { - addRuns(runs, text, 0, textLength); + addRuns(runs, text, 0, textLength, flags); } return runs; } @@ -163,21 +161,21 @@ abstract class LineLayout { return separator.createBorderBetweenTokens(token1, token2); } - private static void addRuns(List runs, char[] text, int start, int end) { + private static void addRuns(List runs, char[] text, int start, int end, int flags) { int afterLastTabPosition = start; for (int i = start; i < end; i++) { if (text[i] == '\t') { - addRunsNoTabs(runs, text, afterLastTabPosition, i); + addRunsNoTabs(runs, text, afterLastTabPosition, i, flags); afterLastTabPosition = i + 1; addOrMergeRun(runs, new BidiRun((byte)0, i, i + 1)); } } - addRunsNoTabs(runs, text, afterLastTabPosition, end); + addRunsNoTabs(runs, text, afterLastTabPosition, end, flags); } - private static void addRunsNoTabs(List runs, char[] text, int start, int end) { + private static void addRunsNoTabs(List runs, char[] text, int start, int end, int flags) { if (start >= end) return; - Bidi bidi = new Bidi(text, start, null, 0, end - start, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + Bidi bidi = new Bidi(text, start, null, 0, end - start, flags); 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))); diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 8d37ab1167ce..a3d2a415536c 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -169,6 +169,12 @@ action.EditorDuplicate.text=Duplicate Line or Selection action.EditorDuplicateLines.text=Duplicate Entire Lines action.EditorSelectWord.text=Extend Selection action.EditorUnSelectWord.text=Shrink Selection +group.EditorBidiTextDirection.text=Bidi Text Direction +group.EditorBidiTextDirection.description=Primary direction for mixed LTR/RTL text +action.EditorSetContentBasedBidiTextDirection.text=Content-based +action.EditorSetContentBasedBidiTextDirection.description=Direction is determined by initial character(s) +action.EditorSetLtrBidiTextDirection.text=Left-to-Right +action.EditorSetRtlBidiTextDirection.text=Right-to-Left action.EditorToggleInsertState.text=Toggle Insert/Overwrite action.EditorToggleColumnMode.text=Column Selection _Mode action.EditorToggleColumnMode.description=Toggle column selection mode in current editor diff --git a/platform/platform-resources/src/idea/PlatformActions.xml b/platform/platform-resources/src/idea/PlatformActions.xml index dfd62a96f23b..f5e1155e4137 100644 --- a/platform/platform-resources/src/idea/PlatformActions.xml +++ b/platform/platform-resources/src/idea/PlatformActions.xml @@ -371,6 +371,14 @@ + + + + + + + +