summaryrefslogtreecommitdiff
path: root/platform/platform-impl/src/com/intellij/openapi/editor
diff options
context:
space:
mode:
Diffstat (limited to 'platform/platform-impl/src/com/intellij/openapi/editor')
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/CaretStateTransferableData.java56
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/ClipboardTextPerCaretSplitter.java68
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretAbove.java23
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretActionHandler.java113
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretBelow.java27
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java76
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorCopyPasteHelperImpl.java150
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java11
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java33
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java132
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java67
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java323
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java2
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java9
-rw-r--r--platform/platform-impl/src/com/intellij/openapi/editor/impl/TrailingSpacesStripper.java216
15 files changed, 1118 insertions, 188 deletions
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/CaretStateTransferableData.java b/platform/platform-impl/src/com/intellij/openapi/editor/CaretStateTransferableData.java
new file mode 100644
index 000000000000..f0d2085cbb6b
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/CaretStateTransferableData.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2000-2014 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;
+
+import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
+
+import java.awt.datatransfer.DataFlavor;
+
+public class CaretStateTransferableData implements TextBlockTransferableData {
+ public static final DataFlavor FLAVOR = new DataFlavor(CaretStateTransferableData.class, "Caret state");
+
+ public final int[] startOffsets;
+ public final int[] endOffsets;
+
+ public CaretStateTransferableData(int[] startOffsets, int[] endOffsets) {
+ this.startOffsets = startOffsets;
+ this.endOffsets = endOffsets;
+ }
+
+ @Override
+ public DataFlavor getFlavor() {
+ return FLAVOR;
+ }
+
+ @Override
+ public int getOffsetCount() {
+ return startOffsets.length + endOffsets.length;
+ }
+
+ @Override
+ public int getOffsets(int[] offsets, int index) {
+ System.arraycopy(startOffsets, 0, offsets, index, startOffsets.length);
+ System.arraycopy(endOffsets, 0, offsets, index + startOffsets.length, endOffsets.length);
+ return index + getOffsetCount();
+ }
+
+ @Override
+ public int setOffsets(int[] offsets, int index) {
+ System.arraycopy(offsets, index, startOffsets, 0, startOffsets.length);
+ System.arraycopy(offsets, index + startOffsets.length, endOffsets, 0, endOffsets.length);
+ return index + getOffsetCount();
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ClipboardTextPerCaretSplitter.java b/platform/platform-impl/src/com/intellij/openapi/editor/ClipboardTextPerCaretSplitter.java
new file mode 100644
index 000000000000..a3e62412f4a4
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/ClipboardTextPerCaretSplitter.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2000-2014 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;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ClipboardTextPerCaretSplitter {
+ @NotNull
+ public List<String> split(@NotNull String input, @Nullable CaretStateTransferableData caretData, int caretCount) {
+ if (caretCount <= 0) {
+ throw new IllegalArgumentException("Caret count must be positive");
+ }
+ if (caretCount == 1) {
+ return Collections.singletonList(input);
+ }
+ List<String> result = new ArrayList<String>(caretCount);
+ int sourceCaretCount = caretData == null ? -1 : caretData.startOffsets.length;
+ String[] lines = sourceCaretCount == 1 || sourceCaretCount == caretCount ? null : input.split("\n", -1);
+ for (int i = 0; i < caretCount; i++) {
+ if (sourceCaretCount == 1) {
+ result.add(input);
+ }
+ else if (sourceCaretCount == caretCount) {
+ //noinspection ConstantConditions
+ result.add(new String(input.substring(caretData.startOffsets[i], caretData.endOffsets[i])));
+ }
+ else if (lines.length == 0) {
+ result.add("");
+ }
+ else if (lines.length == 1) {
+ result.add(lines[0]);
+ }
+ else if (lines.length % caretCount == 0) {
+ StringBuilder b = new StringBuilder();
+ int linesPerSegment = lines.length / caretCount;
+ for (int j = 0; j < linesPerSegment; j++) {
+ if (j > 0) {
+ b.append('\n');
+ }
+ b.append(lines[i * linesPerSegment + j]);
+ }
+ result.add(b.toString());
+ }
+ else {
+ result.add(i < lines.length ? lines[i] : "");
+ }
+ }
+ return result;
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretAbove.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretAbove.java
index f8b3cf7bc83a..c42cd6533e1c 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretAbove.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretAbove.java
@@ -15,31 +15,10 @@
*/
package com.intellij.openapi.editor.actions;
-import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.editor.Caret;
-import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorAction;
-import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
-import org.jetbrains.annotations.NotNull;
public class CloneCaretAbove extends EditorAction {
public CloneCaretAbove() {
- super(new Handler());
- }
-
- private static class Handler extends EditorActionHandler {
- public Handler() {
- super(true);
- }
-
- @Override
- public void doExecute(Editor editor, @NotNull Caret caret, DataContext dataContext) {
- caret.clone(true);
- }
-
- @Override
- public boolean isEnabled(Editor editor, DataContext dataContext) {
- return editor.getCaretModel().supportsMultipleCarets();
- }
+ super(new CloneCaretActionHandler(true));
}
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretActionHandler.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretActionHandler.java
new file mode 100644
index 000000000000..5c5528a38153
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretActionHandler.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2000-2014 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.DataContext;
+import com.intellij.openapi.actionSystem.IdeActions;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.EditorLastActionTracker;
+import com.intellij.openapi.editor.ScrollType;
+import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
+import com.intellij.openapi.util.Key;
+import com.intellij.util.containers.HashSet;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+public class CloneCaretActionHandler extends EditorActionHandler {
+ private static final Key<Integer> LEVEL = Key.create("CloneCaretActionHandler.level");
+
+ private static final Set<String> OUR_ACTIONS = new HashSet<String>(Arrays.asList(
+ IdeActions.ACTION_EDITOR_CLONE_CARET_ABOVE,
+ IdeActions.ACTION_EDITOR_CLONE_CARET_BELOW,
+ IdeActions.ACTION_EDITOR_MOVE_CARET_LEFT_WITH_SELECTION,
+ IdeActions.ACTION_EDITOR_MOVE_CARET_RIGHT_WITH_SELECTION
+ ));
+
+ private final boolean myCloneAbove;
+
+ public CloneCaretActionHandler(boolean above) {
+ myCloneAbove = above;
+ }
+
+ @Override
+ public boolean isEnabled(Editor editor, DataContext dataContext) {
+ return editor.getCaretModel().supportsMultipleCarets();
+ }
+
+ @Override
+ protected void doExecute(Editor editor, @Nullable Caret targetCaret, DataContext dataContext) {
+ if (targetCaret != null) {
+ targetCaret.clone(myCloneAbove);
+ return;
+ }
+ int currentLevel = 0;
+ List<Caret> currentCarets = new ArrayList<Caret>();
+ for (Caret caret : editor.getCaretModel().getAllCarets()) {
+ int level = getLevel(caret);
+ if (Math.abs(level) > Math.abs(currentLevel)) {
+ currentLevel = level;
+ currentCarets.clear();
+ }
+ if (Math.abs(level) == Math.abs(currentLevel)) {
+ currentCarets.add(caret);
+ }
+ }
+ boolean removeCarets = currentLevel > 0 && myCloneAbove || currentLevel < 0 && !myCloneAbove;
+ Integer newLevel = myCloneAbove ? currentLevel - 1 : currentLevel + 1;
+ for (Caret caret : currentCarets) {
+ if (removeCarets) {
+ editor.getCaretModel().removeCaret(caret);
+ }
+ else {
+ Caret clone = caret;
+ do {
+ Caret original = clone;
+ clone = clone.clone(myCloneAbove);
+ if (original != caret) {
+ editor.getCaretModel().removeCaret(original);
+ }
+ } while (clone != null && caret.hasSelection() && !clone.hasSelection());
+ if (clone != null) {
+ clone.putUserData(LEVEL, newLevel);
+ }
+ }
+ }
+ if (removeCarets) {
+ editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
+ }
+ }
+
+ private static int getLevel(Caret caret) {
+ if (isRepeatedActionInvocation()) {
+ Integer value = caret.getUserData(LEVEL);
+ return value == null ? 0 : value;
+ }
+ else {
+ caret.putUserData(LEVEL, null);
+ return 0;
+ }
+ }
+
+ private static boolean isRepeatedActionInvocation() {
+ String lastActionId = EditorLastActionTracker.getInstance().getLastActionId();
+ return OUR_ACTIONS.contains(lastActionId);
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretBelow.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretBelow.java
index 17d17708b900..974087796e08 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretBelow.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/CloneCaretBelow.java
@@ -15,35 +15,10 @@
*/
package com.intellij.openapi.editor.actions;
-import com.intellij.openapi.actionSystem.DataContext;
-import com.intellij.openapi.editor.Caret;
-import com.intellij.openapi.editor.CaretModel;
-import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.EditorAction;
-import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
-import org.jetbrains.annotations.NotNull;
public class CloneCaretBelow extends EditorAction {
public CloneCaretBelow() {
- super(new Handler());
- }
-
- private static class Handler extends EditorActionHandler {
- public Handler() {
- super(true);
- }
-
- @Override
- public void doExecute(Editor editor, @NotNull Caret caret, DataContext dataContext) {
- CaretModel caretModel = editor.getCaretModel();
- if (caretModel.supportsMultipleCarets()) {
- caret.clone(false);
- }
- }
-
- @Override
- public boolean isEnabled(Editor editor, DataContext dataContext) {
- return editor.getCaretModel().supportsMultipleCarets();
- }
+ super(new CloneCaretActionHandler(false));
}
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java
index cfd039a5d504..c769f92fff78 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/CaretImpl.java
@@ -62,7 +62,13 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
private int myVisualLineEnd;
private RangeMarker savedBeforeBulkCaretMarker;
private boolean mySkipChangeRequests;
+ /**
+ * Initial horizontal caret position during vertical navigation.
+ * Similar to {@link #myDesiredX}, but represents logical caret position (<code>getLogicalPosition().column</code>) rather than visual.
+ */
private int myLastColumnNumber = 0;
+ private int myDesiredSelectionStartColumn = -1;
+ private int myDesiredSelectionEndColumn = -1;
/**
* We check that caret is located at the target offset at the end of {@link #moveToOffset(int, boolean)} method. However,
* it's possible that the following situation occurs:
@@ -82,11 +88,10 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
*/
private boolean myReportCaretMoves;
/**
- * There is a possible case that user defined non-monospaced font for editor. That means that various symbols have different
- * visual widths. That means that if we move caret vertically it may deviate to the left/right. However, we can try to preserve
- * its initial visual position when possible.
+ * This field holds initial horizontal caret position during vertical navigation. It's used to determine target position when
+ * moving to the new line. It is stored in pixels, not in columns, to account for non-monospaced fonts as well.
* <p/>
- * This field holds desired value for visual <code>'x'</code> caret coordinate (negative value if no coordinate should be preserved).
+ * Negative value means no coordinate should be preserved.
*/
private int myDesiredX = -1;
@@ -255,10 +260,11 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
EditorSettings editorSettings = myEditor.getSettings();
VisualPosition visualCaret = getVisualPosition();
+ int lastColumnNumber = myLastColumnNumber;
int desiredX = myDesiredX;
if (columnShift == 0) {
if (myDesiredX < 0) {
- desiredX = myEditor.visualPositionToXY(visualCaret).x;
+ desiredX = getCurrentX();
}
}
else {
@@ -267,15 +273,12 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
int newLineNumber = visualCaret.line + lineShift;
int newColumnNumber = visualCaret.column + columnShift;
- if (desiredX >= 0 && !ApplicationManager.getApplication().isUnitTestMode()) {
+ if (desiredX >= 0) {
newColumnNumber = myEditor.xyToVisualPosition(new Point(desiredX, Math.max(0, newLineNumber) * myEditor.getLineHeight())).column;
}
Document document = myEditor.getDocument();
- if (!editorSettings.isVirtualSpace() && columnShift == 0 && getLogicalPosition().softWrapLinesOnCurrentLogicalLine <= 0) {
- newColumnNumber = supportsMultipleCarets() ? myLastColumnNumber : myEditor.getLastColumnNumber();
- }
- else if (!editorSettings.isVirtualSpace() && lineShift == 0 && columnShift == 1) {
+ if (!editorSettings.isVirtualSpace() && lineShift == 0 && columnShift == 1) {
int lastLine = document.getLineCount() - 1;
if (lastLine < 0) lastLine = 0;
if (EditorModificationUtil.calcAfterLineEnd(myEditor) >= 0 &&
@@ -303,18 +306,21 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
// We want to move caret to the first column if it's already located at the first line and 'Up' is pressed.
newColumnNumber = 0;
desiredX = -1;
+ lastColumnNumber = -1;
}
VisualPosition pos = new VisualPosition(newLineNumber, newColumnNumber);
- int lastColumnNumber = newColumnNumber;
if (!myEditor.getSoftWrapModel().isInsideSoftWrap(pos)) {
LogicalPosition log = myEditor.visualToLogicalPosition(new VisualPosition(newLineNumber, newColumnNumber));
int offset = myEditor.logicalPositionToOffset(log);
if (offset >= document.getTextLength()) {
int lastOffsetColumn = myEditor.offsetToVisualPosition(document.getTextLength()).column;
// We want to move caret to the last column if if it's located at the last line and 'Down' is pressed.
- newColumnNumber = lastColumnNumber = Math.max(lastOffsetColumn, newColumnNumber);
- desiredX = -1;
+ if (lastOffsetColumn > newColumnNumber) {
+ newColumnNumber = lastOffsetColumn;
+ desiredX = -1;
+ lastColumnNumber = -1;
+ }
}
if (!editorSettings.isCaretInsideTabs()) {
CharSequence text = document.getCharsSequence();
@@ -353,7 +359,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
}
else {
moveToVisualPosition(pos);
- if (!editorSettings.isVirtualSpace() && columnShift == 0) {
+ if (!editorSettings.isVirtualSpace() && columnShift == 0 && lastColumnNumber >=0) {
setLastColumnNumber(lastColumnNumber);
}
}
@@ -550,6 +556,7 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
}
setLastColumnNumber(myLogicalCaret.column);
+ myDesiredSelectionStartColumn = myDesiredSelectionEndColumn = -1;
myVisibleCaret = myEditor.logicalToVisualPosition(myLogicalCaret);
updateOffsetsFromLogicalPosition();
@@ -705,7 +712,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
myEditor.getFoldingModel().flushCaretPosition();
- setLastColumnNumber(column);
+ setLastColumnNumber(myLogicalCaret.column);
+ myDesiredSelectionStartColumn = myDesiredSelectionEndColumn = -1;
myEditor.updateCaretCursor();
requestRepaint(oldInfo);
@@ -962,6 +970,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
clone.myLastColumnNumber = this.myLastColumnNumber;
clone.myReportCaretMoves = this.myReportCaretMoves;
clone.myDesiredX = this.myDesiredX;
+ clone.myDesiredSelectionStartColumn = -1;
+ clone.myDesiredSelectionEndColumn = -1;
return clone;
}
@@ -971,10 +981,10 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
assertIsDispatchThread();
int lineShift = above ? -1 : 1;
final CaretImpl clone = cloneWithoutSelection();
- final int newSelectionStartOffset, newSelectionEndOffset;
+ final int newSelectionStartOffset, newSelectionEndOffset, newSelectionStartColumn, newSelectionEndColumn;
final VisualPosition newSelectionStartPosition, newSelectionEndPosition;
final boolean hasNewSelection;
- if (hasSelection()) {
+ if (hasSelection() || myDesiredSelectionStartColumn >=0 || myDesiredSelectionEndColumn >= 0) {
VisualPosition startPosition = getSelectionStartPosition();
VisualPosition endPosition = getSelectionEndPosition();
VisualPosition leadPosition = getLeadSelectionPosition();
@@ -982,8 +992,10 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
boolean leadIsEnd = leadPosition.equals(endPosition);
LogicalPosition selectionStart = myEditor.visualToLogicalPosition(leadIsStart || leadIsEnd ? leadPosition : startPosition);
LogicalPosition selectionEnd = myEditor.visualToLogicalPosition(leadIsEnd ? startPosition : endPosition);
- LogicalPosition newSelectionStart = truncate(new LogicalPosition(selectionStart.line + lineShift, selectionStart.column));
- LogicalPosition newSelectionEnd = truncate(new LogicalPosition(selectionEnd.line + lineShift, selectionEnd.column));
+ newSelectionStartColumn = myDesiredSelectionStartColumn < 0 ? selectionStart.column : myDesiredSelectionStartColumn;
+ newSelectionEndColumn = myDesiredSelectionEndColumn < 0 ? selectionEnd.column : myDesiredSelectionEndColumn;
+ LogicalPosition newSelectionStart = truncate(selectionStart.line + lineShift, newSelectionStartColumn);
+ LogicalPosition newSelectionEnd = truncate(selectionEnd.line + lineShift, newSelectionEndColumn);
newSelectionStartOffset = myEditor.logicalPositionToOffset(newSelectionStart);
newSelectionEndOffset = myEditor.logicalPositionToOffset(newSelectionEnd);
newSelectionStartPosition = myEditor.logicalToVisualPosition(newSelectionStart);
@@ -996,6 +1008,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
newSelectionStartPosition = null;
newSelectionEndPosition = null;
hasNewSelection = false;
+ newSelectionStartColumn = -1;
+ newSelectionEndColumn = -1;
}
LogicalPosition oldPosition = getLogicalPosition();
int newLine = oldPosition.line + lineShift;
@@ -1003,7 +1017,12 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
Disposer.dispose(clone);
return null;
}
- clone.moveToLogicalPosition(new LogicalPosition(newLine, oldPosition.column), false, null, false);
+ clone.moveToLogicalPosition(new LogicalPosition(newLine, myLastColumnNumber), false, null, false);
+ clone.myLastColumnNumber = myLastColumnNumber;
+ clone.myDesiredX = myDesiredX >= 0 ? myDesiredX : getCurrentX();
+ clone.myDesiredSelectionStartColumn = newSelectionStartColumn;
+ clone.myDesiredSelectionEndColumn = newSelectionEndColumn;
+
if (myEditor.getCaretModel().addCaret(clone)) {
if (hasNewSelection) {
myEditor.getCaretModel().doWithCaretMerging(new Runnable() {
@@ -1025,15 +1044,15 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
}
}
- private LogicalPosition truncate(LogicalPosition position) {
- if (position.line < 0) {
+ private LogicalPosition truncate(int line, int column) {
+ if (line < 0) {
return new LogicalPosition(0, 0);
}
- else if (position.line >= myEditor.getDocument().getLineCount()) {
+ else if (line >= myEditor.getDocument().getLineCount()) {
return myEditor.offsetToLogicalPosition(myEditor.getDocument().getTextLength());
}
else {
- return position;
+ return new LogicalPosition(line, column);
}
}
@@ -1396,9 +1415,8 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
}
try {
- EditorActionHandler handler = EditorActionManager.getInstance().getActionHandler(
- IdeActions.ACTION_EDITOR_SELECT_WORD_AT_CARET);
- handler.execute(myEditor, myEditor.getDataContext());
+ EditorActionHandler handler = EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_SELECT_WORD_AT_CARET);
+ handler.execute(myEditor, CaretImpl.this, myEditor.getDataContext());
}
finally {
if (needOverrideSetting) {
@@ -1453,6 +1471,10 @@ public class CaretImpl extends UserDataHolderBase implements Caret {
return marker != null && marker.isValid() && isVirtualSelectionEnabled() && myEndVirtualOffset > myStartVirtualOffset;
}
+ private int getCurrentX() {
+ return myEditor.visualPositionToXY(myVisibleCaret).x;
+ }
+
@Override
@NotNull
public EditorImpl getEditor() {
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorCopyPasteHelperImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorCopyPasteHelperImpl.java
new file mode 100644
index 000000000000..c26c7e99ba63
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorCopyPasteHelperImpl.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2000-2014 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.codeInsight.editorActions.TextBlockTransferable;
+import com.intellij.codeInsight.editorActions.TextBlockTransferableData;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.ide.CopyPasteManager;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.text.LineTokenizer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+public class EditorCopyPasteHelperImpl extends EditorCopyPasteHelper {
+ private static final Logger LOG = Logger.getInstance(EditorCopyPasteHelperImpl.class);
+
+ @Override
+ public void copySelectionToClipboard(@NotNull Editor editor) {
+ ApplicationManager.getApplication().assertIsDispatchThread();
+ List<TextBlockTransferableData> extraData = new ArrayList<TextBlockTransferableData>();
+ String s = editor.getCaretModel().supportsMultipleCarets() ? getSelectedTextForClipboard(editor, extraData)
+ : editor.getSelectionModel().getSelectedText();
+ if (s == null) return;
+
+ s = TextBlockTransferable.convertLineSeparators(s, "\n", extraData);
+ Transferable contents = editor.getCaretModel().supportsMultipleCarets() ? new TextBlockTransferable(s, extraData, null) : new StringSelection(s);
+ CopyPasteManager.getInstance().setContents(contents);
+ }
+
+ public static String getSelectedTextForClipboard(@NotNull Editor editor, @NotNull Collection<TextBlockTransferableData> extraDataCollector) {
+ final StringBuilder buf = new StringBuilder();
+ String separator = "";
+ List<Caret> carets = editor.getCaretModel().getAllCarets();
+ int[] startOffsets = new int[carets.size()];
+ int[] endOffsets = new int[carets.size()];
+ for (int i = 0; i < carets.size(); i++) {
+ buf.append(separator);
+ String caretSelectedText = carets.get(i).getSelectedText();
+ startOffsets[i] = buf.length();
+ if (caretSelectedText != null) {
+ buf.append(caretSelectedText);
+ }
+ endOffsets[i] = buf.length();
+ separator = "\n";
+ }
+ extraDataCollector.add(new CaretStateTransferableData(startOffsets, endOffsets));
+ return buf.toString();
+ }
+
+ @Nullable
+ @Override
+ public TextRange[] pasteFromClipboard(@NotNull Editor editor) {
+ CopyPasteManager manager = CopyPasteManager.getInstance();
+ if (manager.areDataFlavorsAvailable(DataFlavor.stringFlavor)) {
+ Transferable clipboardContents = manager.getContents();
+ if (clipboardContents != null) {
+ return pasteTransferable(editor, clipboardContents);
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public TextRange[] pasteTransferable(final @NotNull Editor editor, @NotNull Transferable content) {
+ String text = getStringContent(content);
+ if (text == null) return null;
+
+ if (editor.getCaretModel().supportsMultipleCarets()) {
+ int caretCount = editor.getCaretModel().getCaretCount();
+ if (caretCount == 1 && editor.isColumnMode()) {
+ int pastedLineCount = LineTokenizer.calcLineCount(text, true);
+ EditorModificationUtil.deleteSelectedText(editor);
+ Caret caret = editor.getCaretModel().getPrimaryCaret();
+ for (int i = 0; i < pastedLineCount - 1; i++) {
+ caret = caret.clone(false);
+ if (caret == null) {
+ break;
+ }
+ }
+ caretCount = editor.getCaretModel().getCaretCount();
+ }
+ CaretStateTransferableData caretData = null;
+ try {
+ caretData = content.isDataFlavorSupported(CaretStateTransferableData.FLAVOR)
+ ? (CaretStateTransferableData)content.getTransferData(CaretStateTransferableData.FLAVOR) : null;
+ }
+ catch (Exception e) {
+ LOG.error(e);
+ }
+ final TextRange[] ranges = new TextRange[caretCount];
+ final Iterator<String> segments = new ClipboardTextPerCaretSplitter().split(text, caretData, caretCount).iterator();
+ final int[] index = {0};
+ editor.getCaretModel().runForEachCaret(new CaretAction() {
+ @Override
+ public void perform(Caret caret) {
+ String segment = segments.next();
+ int caretOffset = caret.getOffset();
+ ranges[index[0]++] = new TextRange(caretOffset, caretOffset + segment.length());
+ EditorModificationUtil.insertStringAtCaret(editor, segment, false, true);
+ }
+ });
+ return ranges;
+ }
+ else {
+ int caretOffset = editor.getCaretModel().getOffset();
+ EditorModificationUtil.insertStringAtCaret(editor, text, false, true);
+ return new TextRange[] { new TextRange(caretOffset, caretOffset + text.length())};
+ }
+ }
+
+ @Nullable
+ private static String getStringContent(@NotNull Transferable content) {
+ RawText raw = RawText.fromTransferable(content);
+ if (raw != null) return raw.rawText;
+
+ try {
+ return (String)content.getTransferData(DataFlavor.stringFlavor);
+ }
+ catch (UnsupportedFlavorException ignore) { }
+ catch (IOException ignore) { }
+
+ return null;
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
index f600c606a98d..39706f016adc 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorGutterComponentImpl.java
@@ -992,7 +992,16 @@ class EditorGutterComponentImpl extends EditorGutterComponentEx implements Mouse
}
public void setLineNumberAreaWidth(@NotNull TIntFunction calculator) {
- final int lineNumberAreaWidth = calculator.execute(myLineNumberConvertor.execute(endLineNumber()));
+ int maxLineNumber = 0;
+ for (int i = endLineNumber(); i >= 0; i--) {
+ int number = myLineNumberConvertor.execute(i);
+ if (number >= 0) {
+ maxLineNumber = number;
+ break;
+ }
+ }
+
+ final int lineNumberAreaWidth = calculator.execute(maxLineNumber);
if (myLineNumberAreaWidth != lineNumberAreaWidth) {
myLineNumberAreaWidth = lineNumberAreaWidth;
fireResized();
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java
index c1499a71a71b..8be2284e8360 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorHighlighterCache.java
@@ -15,10 +15,19 @@
*/
package com.intellij.openapi.editor.impl;
+import com.intellij.lexer.Lexer;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter;
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
+import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
+import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.impl.cache.impl.id.PlatformIdTableBuilding;
+import com.intellij.psi.impl.search.LexerEditorHighlighterLexer;
import com.intellij.reference.SoftReference;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -55,4 +64,28 @@ public class EditorHighlighterCache {
return null;
}
+ @Nullable
+ public static Lexer getLexerBasedOnLexerHighlighter(CharSequence text, VirtualFile virtualFile, Project project) {
+ EditorHighlighter highlighter = null;
+
+ PsiFile psiFile = virtualFile != null ? PsiManager.getInstance(project).findFile(virtualFile) : null;
+ final Document document = psiFile != null ? PsiDocumentManager.getInstance(project).getDocument(psiFile) : null;
+ final EditorHighlighter cachedEditorHighlighter;
+ boolean alreadyInitializedHighlighter = false;
+
+ if (document != null &&
+ (cachedEditorHighlighter = getEditorHighlighterForCachesBuilding(document)) != null &&
+ PlatformIdTableBuilding.checkCanUseCachedEditorHighlighter(text, cachedEditorHighlighter)) {
+ highlighter = cachedEditorHighlighter;
+ alreadyInitializedHighlighter = true;
+ }
+ else if (virtualFile != null) {
+ highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(project, virtualFile);
+ }
+
+ if (highlighter != null) {
+ return new LexerEditorHighlighterLexer(highlighter, alreadyInitializedHighlighter);
+ }
+ return null;
+ }
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
index b708f38a9190..c0ffbd10aca4 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java
@@ -33,7 +33,7 @@ import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
import com.intellij.openapi.actionSystem.impl.MouseGestureManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
-import com.intellij.openapi.application.ex.ApplicationManagerEx;
+import com.intellij.openapi.application.impl.LaterInvocator;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.diagnostic.Logger;
@@ -147,7 +147,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
@NotNull private final EditorComponentImpl myEditorComponent;
@NotNull private final EditorGutterComponentImpl myGutterComponent;
private final TraceableDisposable myTraceableDisposable = new TraceableDisposable(new Throwable());
- private volatile boolean hasTabs; // optimisation flag: when editor contains no tabs it is dramatically easier to calculate positions
static {
ComplementaryFontsRegistry.getFontAbleToDisplay(' ', 0, 0, UIManager.getFont("Label.font").getFamily()); // load costly font info
@@ -168,6 +167,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
@NotNull private final CaretCursor myCaretCursor;
private final ScrollingTimer myScrollingTimer = new ScrollingTimer();
+ @SuppressWarnings("RedundantStringConstructorCall")
private final Object MOUSE_DRAGGED_GROUP = new String("MouseDraggedGroup");
@NotNull private final SettingsImpl mySettings;
@@ -246,8 +246,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
@Nullable private Color myForcedBackground = null;
@Nullable private Dimension myPreferredSize;
private int myVirtualPageHeight;
- @Nullable private Runnable myGutterSizeUpdater = null;
- private boolean myGutterNeedsUpdate = false;
private Alarm myAppleRepaintAlarm;
private final Alarm myMouseSelectionStateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
@@ -317,8 +315,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
EditorImpl(@NotNull Document document, boolean viewer, @Nullable Project project) {
+ assertIsDispatchThread();
myProject = project;
myDocument = (DocumentEx)document;
+ if (myDocument instanceof DocumentImpl) {
+ ((DocumentImpl)myDocument).requestTabTracking();
+ }
myScheme = createBoundColorSchemeDelegate(null);
initTabPainter();
myIsViewer = viewer;
@@ -433,13 +435,13 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
if (myPrimaryCaret != null) {
myPrimaryCaret.updateVisualPosition(); // repainting old primary caret's row background
}
- ((CaretImpl)e.getCaret()).updateVisualPosition(); // repainting caret region
+ updateCaretVisualPosition(e);
myPrimaryCaret = myCaretModel.getPrimaryCaret();
}
@Override
public void caretRemoved(CaretEvent e) {
- ((CaretImpl)e.getCaret()).updateVisualPosition(); // repainting caret region
+ updateCaretVisualPosition(e);
myPrimaryCaret = myCaretModel.getPrimaryCaret(); // repainting new primary caret's row background
myPrimaryCaret.updateVisualPosition();
}
@@ -548,7 +550,13 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
});
}
- updateHasTabsFlag(document.getImmutableCharSequence());
+ }
+
+ private static void updateCaretVisualPosition(CaretEvent e) {
+ CaretImpl caretImpl = ((CaretImpl)e.getCaret());
+ if (caretImpl != null) {
+ caretImpl.updateVisualPosition(); // repainting caret region
+ }
}
@NotNull
@@ -575,7 +583,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
myPrefixWidthInPixels = 0;
if (myPrefixText != null) {
for (char c : myPrefixText) {
- myPrefixWidthInPixels += EditorUtil.charWidth(c, myPrefixAttributes.getFontType(), this);
+ LOG.assertTrue(myPrefixAttributes != null);
+ if (myPrefixAttributes != null) {
+ myPrefixWidthInPixels += EditorUtil.charWidth(c, myPrefixAttributes.getFontType(), this);
+ }
}
}
mySoftWrapModel.recalculate();
@@ -791,6 +802,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
if (myConnection != null) {
myConnection.disconnect();
}
+ if (myDocument instanceof DocumentImpl) {
+ ((DocumentImpl)myDocument).giveUpTabTracking();
+ }
Disposer.dispose(myDisposable);
}
@@ -898,9 +912,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
new UiNotifyConnector(myEditorComponent, new Activatable.Adapter(){
@Override
public void showNotify() {
- if (myGutterNeedsUpdate) {
- updateGutterSize();
- }
+ myGutterComponent.updateSize();
}
});
@@ -1505,7 +1517,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private final int[] myLastXOffsets = new int[myLastStartOffsets.length];
private final int[] myLastXs = new int[myLastStartOffsets.length];
private int myCurrentCachePosition;
- private int myLastCacheHits, myTotalRequests; // todo remove
+ private int myLastCacheHits;
+ private int myTotalRequests; // todo remove
private int getTabbedTextWidth(int startOffset, int targetColumn, int xOffset) {
int x = xOffset;
@@ -1621,7 +1634,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
@Override
public void repaint(final int startOffset, int endOffset) {
- if (!isShowing() || myScrollPane == null || myDocument.isInBulkUpdate()) {
+ if (!isShowing() || myDocument.isInBulkUpdate()) {
return;
}
@@ -1638,7 +1651,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
private boolean isShowing() {
- return myGutterComponent != null && myGutterComponent.isShowing();
+ return myGutterComponent.isShowing();
}
private void repaintToScreenBottom(int startLine) {
@@ -1679,9 +1692,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
private void bulkUpdateFinished() {
- if (myScrollPane == null) {
- return;
- }
clearTextWidthCache();
@@ -1700,7 +1710,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
if (isStickySelection()) {
setStickySelection(false);
}
- if (myDocument.isInBulkUpdate() || myScrollingModel == null) {
+ if (myDocument.isInBulkUpdate()) {
// Assuming that the job is done at bulk listener callback methods.
return;
}
@@ -1716,7 +1726,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
private void changedUpdate(DocumentEvent e) {
- if (myScrollPane == null || myDocument.isInBulkUpdate()) return;
+ if (myDocument.isInBulkUpdate()) return;
clearTextWidthCache();
mySelectionModel.removeBlockSelection();
@@ -1754,16 +1764,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
Point caretLocation = visualPositionToXY(getCaretModel().getVisualPosition());
int scrollOffset = caretLocation.y - myCaretUpdateVShift;
getScrollingModel().scrollVertically(scrollOffset);
- updateHasTabsFlag(e.getNewFragment());
}
- private void updateHasTabsFlag(@NotNull CharSequence newChars) {
- if (!hasTabs) {
- hasTabs = StringUtil.contains(newChars, 0, newChars.length(), '\t');
- }
- }
public boolean hasTabs() {
- return hasTabs;
+ return !(myDocument instanceof DocumentImpl) || ((DocumentImpl)myDocument).mightContainTabs();
}
public boolean isScrollToCaret() {
@@ -1784,24 +1788,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
private void updateGutterSize() {
- if (myGutterSizeUpdater != null) return;
- myGutterSizeUpdater = new Runnable() {
+ LaterInvocator.invokeLater(new Runnable() {
@Override
public void run() {
- if (!isDisposed()) {
- if (isShowing()) {
- myGutterComponent.updateSize();
- myGutterNeedsUpdate = false;
- }
- else {
- myGutterNeedsUpdate = true;
- }
- }
- myGutterSizeUpdater = null;
+ myGutterComponent.updateSize();
}
- };
-
- SwingUtilities.invokeLater(myGutterSizeUpdater);
+ });
}
void validateSize() {
@@ -1945,7 +1937,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
if (isReleased) {
- g.setColor(new Color(128, 255, 128));
+ g.setColor(new JBColor(new Color(128, 255, 128), new Color(128, 255, 128)));
g.fillRect(clip.x, clip.y, clip.width, clip.height);
return;
}
@@ -2239,7 +2231,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
myLastBackgroundColor = null;
int start = clipStartOffset;
- int end = clipEndOffset;
+
if (!myPurePaintingMode) {
getSoftWrapModel().registerSoftWrapsIfNecessary();
}
@@ -2250,7 +2242,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return;
}
- IterationState iterationState = new IterationState(this, start, end, isPaintSelection());
+ IterationState iterationState = new IterationState(this, start, clipEndOffset, isPaintSelection());
TextAttributes attributes = iterationState.getMergedAttributes();
Color backColor = getBackgroundColor(attributes);
int fontType = attributes.getFontType();
@@ -2322,7 +2314,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
position.y += lineHeight;
start = lEnd;
}
- else if (collapsedFolderAt.getEndOffset() == end) {
+ else if (collapsedFolderAt.getEndOffset() == clipEndOffset) {
softWrap = mySoftWrapModel.getSoftWrap(collapsedFolderAt.getStartOffset());
if (softWrap != null) {
position.x = drawSoftWrapAwareBackground(
@@ -3677,19 +3669,16 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return logicalToVisualPosition(logicalPos, true);
}
- // TODO den remove as soon as the problem is fixed.
- private final ThreadLocal<Integer> stackDepth = new ThreadLocal<Integer>();
-
- // TODO den remove as soon as the problem is fixed.
@Override
@NotNull
public VisualPosition logicalToVisualPosition(@NotNull LogicalPosition logicalPos, boolean softWrapAware) {
- stackDepth.set(0);
- return doLogicalToVisualPosition(logicalPos, softWrapAware);
+ return doLogicalToVisualPosition(logicalPos, softWrapAware,0);
}
@NotNull
- private VisualPosition doLogicalToVisualPosition(@NotNull LogicalPosition logicalPos, boolean softWrapAware) {
+ private VisualPosition doLogicalToVisualPosition(@NotNull LogicalPosition logicalPos, boolean softWrapAware,
+ // TODO den remove as soon as the problem is fixed.
+ int stackDepth) {
assertReadAccess();
if (!myFoldingModel.isFoldingEnabled() && !mySoftWrapModel.isSoftWrappingEnabled()) {
return new VisualPosition(logicalPos.line, logicalPos.column);
@@ -3703,25 +3692,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
offset = outermostCollapsed.getStartOffset();
LogicalPosition foldStart = offsetToLogicalPosition(offset);
// TODO den remove as soon as the problem is fixed.
- Integer depth = stackDepth.get();
- if (depth >= 0) {
- stackDepth.set(depth + 1);
- if (depth > 15) {
- LOG.error("Detected potential StackOverflowError at logical->visual position mapping. Given logical position: '" +
- logicalPos + "'. State: " + dumpState());
- stackDepth.set(-1);
- }
- }
- // TODO den remove as soon as the problem is fixed.
- try {
- return doLogicalToVisualPosition(foldStart, true);
- }
- finally {
- depth = stackDepth.get();
- if (depth > 0) {
- stackDepth.set(depth - 1);
- }
+ if (stackDepth > 15) {
+ LOG.error("Detected potential StackOverflowError at logical->visual position mapping. Given logical position: '" +
+ logicalPos + "'. State: " + dumpState());
+ stackDepth = -1;
}
+ return doLogicalToVisualPosition(foldStart, true, stackDepth+1);
}
else {
offset = outermostCollapsed.getEndOffset() + 3; // WTF?
@@ -3988,7 +3964,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
return false;
}
- if (e.getComponent() != myInitialMouseEvent.getComponent() || !e.getPoint().equals(myInitialMouseEvent.getPoint())) {
+ if (myInitialMouseEvent!= null && (e.getComponent() != myInitialMouseEvent.getComponent() || !e.getPoint().equals(myInitialMouseEvent.getPoint()))) {
myIgnoreMouseEventsConsecutiveToInitial = false;
myInitialMouseEvent = null;
return false;
@@ -4197,7 +4173,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
} else {
final LogicalPosition blockStart = selectionModel.hasBlockSelection() ? selectionModel.getBlockStart() : oldLogicalCaret;
- setBlockSelectionAndBlockActions(e, blockStart, getCaretModel().getLogicalPosition());
+ if (blockStart != null) {
+ setBlockSelectionAndBlockActions(e, blockStart, getCaretModel().getLogicalPosition());
+ }
}
}
else {
@@ -5002,7 +4980,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
}
void assertIsDispatchThread() {
- ApplicationManagerEx.getApplicationEx().assertIsDispatchThread();
+ ApplicationManager.getApplication().assertIsDispatchThread();
}
private static void assertReadAccess() {
@@ -5392,7 +5370,9 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
public void run() {
int docLength = doc.getTextLength();
ProperTextRange range = composedTextRange.intersection(new TextRange(0, docLength));
- doc.deleteString(range.getStartOffset(), range.getEndOffset());
+ if (range != null) {
+ doc.deleteString(range.getStartOffset(), range.getEndOffset());
+ }
}
});
}
@@ -6745,7 +6725,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
private class TablessBorder extends SideBorder {
private TablessBorder() {
- super(UIUtil.getBorderColor(), SideBorder.ALL);
+ super(JBColor.border(), SideBorder.ALL);
}
@Override
@@ -6769,7 +6749,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi
public Insets getBorderInsets(Component c) {
Container splitters = SwingUtilities.getAncestorOfClass(EditorsSplitters.class, c);
boolean thereIsSomethingAbove = !SystemInfo.isMac || UISettings.getInstance().SHOW_MAIN_TOOLBAR || UISettings.getInstance().SHOW_NAVIGATION_BAR ||
- EditorImpl.this.myProject != null && !ToolWindowManagerEx.getInstanceEx(EditorImpl.this.myProject).getIdsOn(ToolWindowAnchor.TOP).isEmpty();
+ myProject != null && !ToolWindowManagerEx.getInstanceEx(myProject).getIdsOn(ToolWindowAnchor.TOP).isEmpty();
return splitters == null ? super.getBorderInsets(c) : new Insets(thereIsSomethingAbove ? 1 : 0, 0, 0, 0);
}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java
index a2f45fe33857..6ec29e58dc0b 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java
@@ -36,6 +36,7 @@ import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId;
+import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorFontType;
import com.intellij.openapi.editor.ex.*;
import com.intellij.openapi.editor.markup.ErrorStripeRenderer;
@@ -45,7 +46,7 @@ import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.ProperTextRange;
-import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
import com.intellij.ui.*;
@@ -428,7 +429,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
}
@Override
- public void paint(Graphics g) {
+ public void paint(@NotNull Graphics g) {
((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart();
final Rectangle bounds = getBounds();
@@ -437,7 +438,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
errorIconBounds.y = bounds.height / 2 - errorIconBounds.height / 2;
try {
- if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) {
+ if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) {
g.setColor(getEditor().getColorsScheme().getDefaultBackground());
g.fillRect(0, 0, bounds.width, bounds.height);
@@ -493,7 +494,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
}
@Override
- public void uninstallUI(JComponent c) {
+ public void uninstallUI(@NotNull JComponent c) {
super.uninstallUI(c);
myCachedTrack = null;
}
@@ -528,7 +529,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
@Override
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
- if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) {
+ if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) {
super.paintThumb(g, c, thumbBounds);
return;
}
@@ -565,13 +566,13 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
@Override
protected int getThickness() {
- if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) return super.getThickness();
+ if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) return super.getThickness();
return super.getThickness() + (isMacOverlayScrollbar() ? 2 : 7);
}
@Override
protected void doPaintTrack(Graphics g, JComponent c, Rectangle bounds) {
- if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) {
+ if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) {
g.setColor(getEditor().getColorsScheme().getDefaultBackground());
g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
//return;
@@ -609,7 +610,9 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
}
private void paintTrackBasement(Graphics g, Rectangle bounds) {
- if (UISettings.getInstance().PRESENTATION_MODE || SystemInfo.isMac) {
+ if (UISettings.getInstance().PRESENTATION_MODE || ButtonlessScrollBarUI.isMacOverlayScrollbarSupported()) {
+ g.setColor(EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground());
+ g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
return;
}
@@ -647,13 +650,13 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
private void drawMarkup(final Graphics g, final int width, int startOffset, int endOffset, MarkupModelEx markup) {
final Queue<PositionedStripe> thinEnds = new PriorityQueue<PositionedStripe>(5, new Comparator<PositionedStripe>() {
@Override
- public int compare(PositionedStripe o1, PositionedStripe o2) {
+ public int compare(@NotNull PositionedStripe o1, @NotNull PositionedStripe o2) {
return o1.yEnd - o2.yEnd;
}
});
final Queue<PositionedStripe> wideEnds = new PriorityQueue<PositionedStripe>(5, new Comparator<PositionedStripe>() {
@Override
- public int compare(PositionedStripe o1, PositionedStripe o2) {
+ public int compare(@NotNull PositionedStripe o1, @NotNull PositionedStripe o2) {
return o1.yEnd - o2.yEnd;
}
});
@@ -757,12 +760,21 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
boolean drawBottomDecoration) {
int x = isMirrored() ? 3 : 5;
int paintWidth = width;
+ boolean flatStyle = Registry.is("ide.new.markup.markers");
if (thinErrorStripeMark) {
paintWidth /= 2;
- paintWidth += 1;
+ paintWidth += flatStyle ? 0 : 1;
x = isMirrored() ? width + 2 : 0;
}
if (color == null) return;
+ Color darker = UIUtil.isUnderDarcula()? color : ColorUtil.shift(color, 0.75);
+
+ if (flatStyle) {
+ g.setColor(darker);
+ g.fillRect(x, yStart, paintWidth, yEnd - yStart + 1);
+ return;
+ }
+
g.setColor(color);
g.fillRect(x + 1, yStart, paintWidth - 2, yEnd - yStart + 1);
@@ -774,7 +786,6 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
//top decoration
UIUtil.drawLine(g, x + 1, yStart, x + paintWidth - 2, yStart);
}
- Color darker = ColorUtil.shift(color, 0.75);
g.setColor(darker);
if (drawBottomDecoration) {
@@ -787,7 +798,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
// mouse events
@Override
- public void mouseClicked(final MouseEvent e) {
+ public void mouseClicked(@NotNull final MouseEvent e) {
CommandProcessor.getInstance().executeCommand(myEditor.getProject(), new Runnable() {
@Override
public void run() {
@@ -801,11 +812,11 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
}
@Override
- public void mousePressed(MouseEvent e) {
+ public void mousePressed(@NotNull MouseEvent e) {
}
@Override
- public void mouseReleased(MouseEvent e) {
+ public void mouseReleased(@NotNull MouseEvent e) {
}
private int getWidth() {
@@ -824,7 +835,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
}
@Override
- public void mouseMoved(MouseEvent e) {
+ public void mouseMoved(@NotNull MouseEvent e) {
EditorImpl.MyScrollBar scrollBar = myEditor.getVerticalScrollBar();
int buttonHeight = scrollBar.getDecScrollButtonHeight();
int lineCount = getDocument().getLineCount() + myEditor.getSettings().getAdditionalLinesCount();
@@ -897,16 +908,16 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
}
@Override
- public void mouseEntered(MouseEvent e) {
+ public void mouseEntered(@NotNull MouseEvent e) {
}
@Override
- public void mouseExited(MouseEvent e) {
+ public void mouseExited(@NotNull MouseEvent e) {
cancelMyToolTips(e, true);
}
@Override
- public void mouseDragged(MouseEvent e) {
+ public void mouseDragged(@NotNull MouseEvent e) {
cancelMyToolTips(e, true);
}
@@ -943,13 +954,15 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
}
public void markDirtied(@NotNull ProperTextRange yPositions) {
- int start = Math.max(0, yPositions.getStartOffset() - myEditor.getLineHeight());
- int end = myEditorScrollbarTop + myEditorTargetHeight == 0 ? yPositions.getEndOffset() + myEditor.getLineHeight()
- : Math
- .min(myEditorScrollbarTop + myEditorTargetHeight, yPositions.getEndOffset() + myEditor.getLineHeight());
- ProperTextRange adj = new ProperTextRange(start, Math.max(end, start));
+ if (myDirtyYPositions != WHOLE_DOCUMENT) {
+ int start = Math.max(0, yPositions.getStartOffset() - myEditor.getLineHeight());
+ int end = myEditorScrollbarTop + myEditorTargetHeight == 0 ? yPositions.getEndOffset() + myEditor.getLineHeight()
+ : Math
+ .min(myEditorScrollbarTop + myEditorTargetHeight, yPositions.getEndOffset() + myEditor.getLineHeight());
+ ProperTextRange adj = new ProperTextRange(start, Math.max(end, start));
- myDirtyYPositions = myDirtyYPositions == null ? adj : myDirtyYPositions.union(adj);
+ myDirtyYPositions = myDirtyYPositions == null ? adj : myDirtyYPositions.union(adj);
+ }
myEditorScrollbarTop = 0;
myEditorSourceHeight = 0;
@@ -1137,7 +1150,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
myHighlighters.add(rangeHighlighter);
}
Collections.sort(myHighlighters, new Comparator<RangeHighlighterEx>() {
- public int compare(RangeHighlighterEx ex1, RangeHighlighterEx ex2) {
+ public int compare(@NotNull RangeHighlighterEx ex1, @NotNull RangeHighlighterEx ex2) {
LogicalPosition startPos1 = myEditor.offsetToLogicalPosition(ex1.getAffectedAreaStartOffset());
LogicalPosition startPos2 = myEditor.offsetToLogicalPosition(ex2.getAffectedAreaStartOffset());
if (startPos1.line != startPos2.line) return 0;
@@ -1167,7 +1180,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
}
@Override
- protected void paintComponent(Graphics g) {
+ protected void paintComponent(@NotNull Graphics g) {
if (myVisualLine ==-1) return;
Dimension size = getPreferredSize();
EditorGutterComponentEx gutterComponentEx = myEditor.getGutterComponentEx();
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java
new file mode 100644
index 000000000000..5a9c704173aa
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2000-2014 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.codeStyle.CodeStyleFacade;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.EditorFactory;
+import com.intellij.openapi.editor.LazyRangeMarkerFactory;
+import com.intellij.openapi.editor.RangeMarker;
+import com.intellij.openapi.editor.event.DocumentAdapter;
+import com.intellij.openapi.editor.event.DocumentEvent;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.UserDataHolderBase;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.util.containers.WeakList;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+public class LazyRangeMarkerFactoryImpl extends LazyRangeMarkerFactory {
+ private final Project myProject;
+ private static final Key<WeakList<LazyMarker>> LAZY_MARKERS_KEY = Key.create("LAZY_MARKERS_KEY");
+
+ public LazyRangeMarkerFactoryImpl(@NotNull Project project, @NotNull final FileDocumentManager fileDocumentManager) {
+ myProject = project;
+
+ EditorFactory.getInstance().getEventMulticaster().addDocumentListener(new DocumentAdapter() {
+ @Override
+ public void beforeDocumentChange(DocumentEvent e) {
+ transformRangeMarkers(e);
+ }
+
+ @Override
+ public void documentChanged(DocumentEvent e) {
+ transformRangeMarkers(e);
+ }
+
+ private void transformRangeMarkers(@NotNull DocumentEvent e) {
+ Document document = e.getDocument();
+ VirtualFile file = fileDocumentManager.getFile(document);
+ if (file == null) {
+ return;
+ }
+
+ WeakList<LazyMarker> lazyMarkers = getMarkers(file);
+ if (lazyMarkers == null) {
+ return;
+ }
+
+ List<LazyMarker> markers = lazyMarkers.toStrongList();
+ for (LazyMarker marker : markers) {
+ if (file.equals(marker.getFile())) {
+ marker.getOrCreateDelegate();
+ }
+ }
+ }
+ }, project);
+ }
+
+ static WeakList<LazyMarker> getMarkers(@NotNull VirtualFile file) {
+ return file.getUserData(LazyRangeMarkerFactoryImpl.LAZY_MARKERS_KEY);
+ }
+
+ private static void addToLazyMarkersList(@NotNull LazyMarker marker, @NotNull VirtualFile file) {
+ WeakList<LazyMarker> markers = getMarkers(file);
+
+ if (markers == null) {
+ markers = file.putUserDataIfAbsent(LAZY_MARKERS_KEY, new WeakList<LazyMarker>());
+ }
+ markers.add(marker);
+ }
+
+ private static void removeFromLazyMarkersList(@NotNull LazyMarker marker, @NotNull VirtualFile file) {
+ WeakList<LazyMarker> markers = getMarkers(file);
+
+ if (markers != null) {
+ markers.remove(marker);
+ }
+ }
+
+ @Override
+ @NotNull
+ public RangeMarker createRangeMarker(@NotNull final VirtualFile file, final int offset) {
+ return ApplicationManager.getApplication().runReadAction(new Computable<RangeMarker>() {
+ @Override
+ public RangeMarker compute() {
+ // even for already loaded document do not create range marker yet - wait until it really needed when e.g. user clicked to jump to OpenFileDescriptor
+ final LazyMarker marker = new OffsetLazyMarker(file, offset);
+ addToLazyMarkersList(marker, file);
+ return marker;
+ }
+ });
+ }
+
+ @Override
+ @NotNull
+ public RangeMarker createRangeMarker(@NotNull final VirtualFile file, final int line, final int column, final boolean persistent) {
+ return ApplicationManager.getApplication().runReadAction(new Computable<RangeMarker>() {
+ @Override
+ public RangeMarker compute() {
+ final Document document = FileDocumentManager.getInstance().getCachedDocument(file);
+ if (document != null) {
+ final int offset = calculateOffset(myProject, file, document, line, column);
+ return document.createRangeMarker(offset, offset, persistent);
+ }
+
+ final LazyMarker marker = new LineColumnLazyMarker(file, line, column);
+ addToLazyMarkersList(marker, file);
+ return marker;
+ }
+ });
+ }
+
+ abstract static class LazyMarker extends UserDataHolderBase implements RangeMarker {
+ protected RangeMarker myDelegate; // the real range marker which is created only when document is opened, or (this) which means it's disposed
+ protected final VirtualFile myFile;
+ protected final int myInitialOffset;
+
+ private LazyMarker(@NotNull VirtualFile file, int offset) {
+ myFile = file;
+ myInitialOffset = offset;
+ }
+
+ boolean isDelegated() {
+ return myDelegate != null;
+ }
+
+ @NotNull
+ public VirtualFile getFile() {
+ return myFile;
+ }
+
+ @Nullable
+ protected final RangeMarker getOrCreateDelegate() {
+ if (myDelegate == null) {
+ Document document = FileDocumentManager.getInstance().getDocument(myFile);
+ if (document == null) {
+ return null;
+ }
+ myDelegate = createDelegate(myFile, document);
+ removeFromLazyMarkersList(this, myFile);
+ }
+ return isDisposed() ? null : myDelegate;
+ }
+
+ @Nullable
+ protected abstract RangeMarker createDelegate(@NotNull VirtualFile file, @NotNull Document document);
+
+ @Override
+ @NotNull
+ public Document getDocument() {
+ RangeMarker delegate = getOrCreateDelegate();
+ if (delegate == null) {
+ //noinspection ConstantConditions
+ return FileDocumentManager.getInstance().getDocument(myFile);
+ }
+ return delegate.getDocument();
+ }
+
+ @Override
+ public int getStartOffset() {
+ return myDelegate == null || isDisposed() ? myInitialOffset : myDelegate.getStartOffset();
+ }
+
+ public boolean isDisposed() {
+ return myDelegate == this;
+ }
+
+
+ @Override
+ public int getEndOffset() {
+ return myDelegate == null || isDisposed() ? myInitialOffset : myDelegate.getEndOffset();
+ }
+
+ @Override
+ public boolean isValid() {
+ RangeMarker delegate = getOrCreateDelegate();
+ return delegate != null && !isDisposed() && delegate.isValid();
+ }
+
+ @Override
+ public void setGreedyToLeft(boolean greedy) {
+ getOrCreateDelegate().setGreedyToLeft(greedy);
+ }
+
+ @Override
+ public void setGreedyToRight(boolean greedy) {
+ getOrCreateDelegate().setGreedyToRight(greedy);
+ }
+
+ @Override
+ public boolean isGreedyToRight() {
+ return getOrCreateDelegate().isGreedyToRight();
+ }
+
+ @Override
+ public boolean isGreedyToLeft() {
+ return getOrCreateDelegate().isGreedyToLeft();
+ }
+
+ @Override
+ public void dispose() {
+ assert !isDisposed();
+ RangeMarker delegate = myDelegate;
+ if (delegate == null) {
+ removeFromLazyMarkersList(this, myFile);
+ myDelegate = this; // mark of disposed marker
+ }
+ else {
+ delegate.dispose();
+ }
+ }
+ }
+
+ private static class OffsetLazyMarker extends LazyMarker {
+ private OffsetLazyMarker(@NotNull VirtualFile file, int offset) {
+ super(file, offset);
+ }
+
+ @Override
+ public boolean isValid() {
+ RangeMarker delegate = myDelegate;
+ if (delegate == null) {
+ Document document = FileDocumentManager.getInstance().getDocument(myFile);
+ return document != null;
+ }
+
+ return super.isValid();
+ }
+
+ @Override
+ @NotNull
+ public RangeMarker createDelegate(@NotNull VirtualFile file, @NotNull final Document document) {
+ final int offset = Math.min(myInitialOffset, document.getTextLength());
+ return document.createRangeMarker(offset, offset);
+ }
+ }
+
+ private class LineColumnLazyMarker extends LazyMarker {
+ private final int myLine;
+ private final int myColumn;
+
+ private LineColumnLazyMarker(@NotNull VirtualFile file, int line, int column) {
+ super(file, -1);
+ myLine = line;
+ myColumn = column;
+ }
+
+ @Override
+ @Nullable
+ public RangeMarker createDelegate(@NotNull VirtualFile file, @NotNull Document document) {
+ if (document.getTextLength() == 0 && !(myLine == 0 && myColumn == 0)) {
+ return null;
+ }
+
+ int offset = calculateOffset(myProject, file, document, myLine, myColumn);
+ return document.createRangeMarker(offset, offset);
+ }
+
+ @Override
+ public boolean isValid() {
+ RangeMarker delegate = myDelegate;
+ if (delegate == null) {
+ Document document = FileDocumentManager.getInstance().getDocument(myFile);
+ return document != null && (document.getTextLength() != 0 || myLine == 0 && myColumn == 0);
+ }
+
+ return super.isValid();
+ }
+
+ @Override
+ public int getStartOffset() {
+ getOrCreateDelegate();
+ return super.getStartOffset();
+ }
+
+ @Override
+ public int getEndOffset() {
+ getOrCreateDelegate();
+ return super.getEndOffset();
+ }
+ }
+
+ private static int calculateOffset(@NotNull Project project, @NotNull VirtualFile file, @NotNull Document document, final int line, final int column) {
+ int offset;
+ if (line < document.getLineCount()) {
+ final int lineStart = document.getLineStartOffset(line);
+ final int lineEnd = document.getLineEndOffset(line);
+ final CharSequence docText = document.getCharsSequence();
+ final int tabSize = CodeStyleFacade.getInstance(project).getTabSize(file.getFileType());
+
+ offset = lineStart;
+ int col = 0;
+ while (offset < lineEnd && col < column) {
+ col += docText.charAt(offset) == '\t' ? tabSize : 1;
+ offset++;
+ }
+ }
+ else {
+ offset = document.getTextLength();
+ }
+ return offset;
+ }
+
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java
index 2f5a81153579..731d1e26a289 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java
@@ -567,7 +567,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi
@Override
public void copySelectionToClipboard() {
- CopyPasteSupport.copySelectionToClipboard(myEditor);
+ EditorCopyPasteHelper.getInstance().copySelectionToClipboard(myEditor);
}
@Override
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java
index 6f1b9820132b..d8b6697dc8d1 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java
@@ -303,14 +303,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx, PrioritizedDocumentLi
if (!isSoftWrappingEnabled()) {
return 0;
}
- int result = 0;
- FoldingModel foldingModel = myEditor.getFoldingModel();
- for (SoftWrap softWrap : myStorage.getSoftWraps()) {
- if (!foldingModel.isOffsetCollapsed(softWrap.getStart())) {
- result++; // Assuming that soft wrap has single line feed all the time
- }
- }
- return result;
+ return myStorage.getSoftWraps().size(); // Assuming that soft wrap has single line feed all the time
}
/**
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/TrailingSpacesStripper.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/TrailingSpacesStripper.java
new file mode 100644
index 000000000000..165ff5776756
--- /dev/null
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/TrailingSpacesStripper.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2000-2014 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.ide.DataManager;
+import com.intellij.injected.editor.DocumentWindow;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.editor.*;
+import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.ShutDownTracker;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.wm.IdeFocusManager;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.text.CharArrayUtil;
+import gnu.trove.THashSet;
+import org.jetbrains.annotations.NotNull;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+public final class TrailingSpacesStripper extends FileDocumentManagerAdapter {
+ public static final Key<String> OVERRIDE_STRIP_TRAILING_SPACES_KEY = Key.create("OVERRIDE_TRIM_TRAILING_SPACES_KEY");
+ public static final Key<Boolean> OVERRIDE_ENSURE_NEWLINE_KEY = Key.create("OVERRIDE_ENSURE_NEWLINE_KEY");
+
+ private final Set<Document> myDocumentsToStripLater = new THashSet<Document>();
+
+ @Override
+ public void beforeAllDocumentsSaving() {
+ Set<Document> documentsToStrip = new THashSet<Document>(myDocumentsToStripLater);
+ myDocumentsToStripLater.clear();
+ for (Document document : documentsToStrip) {
+ strip(document);
+ }
+ }
+
+ @Override
+ public void beforeDocumentSaving(@NotNull Document document) {
+ strip(document);
+ }
+
+ private void strip(@NotNull final Document document) {
+ if (!document.isWritable()) return;
+ FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
+ VirtualFile file = fileDocumentManager.getFile(document);
+ if (file == null || !file.isValid()) return;
+
+ final EditorSettingsExternalizable settings = EditorSettingsExternalizable.getInstance();
+ if (settings == null) return;
+
+ final String overrideStripTrailingSpacesData = file.getUserData(OVERRIDE_STRIP_TRAILING_SPACES_KEY);
+ final Boolean overrideEnsureNewlineData = file.getUserData(OVERRIDE_ENSURE_NEWLINE_KEY);
+ final String stripTrailingSpaces = overrideStripTrailingSpacesData != null ? overrideStripTrailingSpacesData : settings.getStripTrailingSpaces();
+ final boolean doStrip = !stripTrailingSpaces.equals(EditorSettingsExternalizable.STRIP_TRAILING_SPACES_NONE);
+ final boolean ensureEOL = overrideEnsureNewlineData != null ? overrideEnsureNewlineData.booleanValue() : settings.isEnsureNewLineAtEOF();
+
+ if (doStrip) {
+ final boolean inChangedLinesOnly = !stripTrailingSpaces.equals(EditorSettingsExternalizable.STRIP_TRAILING_SPACES_WHOLE);
+ boolean success = stripIfNotCurrentLine(document, inChangedLinesOnly);
+ if (!success) {
+ myDocumentsToStripLater.add(document);
+ }
+ }
+
+ final int lines = document.getLineCount();
+ if (ensureEOL && lines > 0) {
+ final int start = document.getLineStartOffset(lines - 1);
+ final int end = document.getLineEndOffset(lines - 1);
+ if (start != end) {
+ final CharSequence content = document.getCharsSequence();
+ ApplicationManager.getApplication().runWriteAction(new DocumentRunnable(document, null) {
+ @Override
+ public void run() {
+ CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
+ @Override
+ public void run() {
+ if (CharArrayUtil.containsOnlyWhiteSpaces(content.subSequence(start, end)) && doStrip) {
+ document.deleteString(start, end);
+ }
+ else {
+ document.insertString(end, "\n");
+ }
+ }
+ });
+ }
+ });
+ }
+ }
+ }
+
+ // clears line modification flags except lines which was not stripped because the caret was in the way
+ public void clearLineModificationFlags(@NotNull Document document) {
+ if (document instanceof DocumentWindow) {
+ document = ((DocumentWindow)document).getDelegate();
+ }
+ if (!(document instanceof DocumentImpl)) {
+ return;
+ }
+
+ Component focusOwner = IdeFocusManager.getGlobalInstance().getFocusOwner();
+ DataContext dataContext = DataManager.getInstance().getDataContext(focusOwner);
+ boolean isDisposeInProgress = ApplicationManager.getApplication().isDisposeInProgress(); // ignore caret placing when exiting
+ Editor activeEditor = isDisposeInProgress ? null : CommonDataKeys.EDITOR.getData(dataContext);
+
+ // when virtual space enabled, we can strip whitespace anywhere
+ boolean isVirtualSpaceEnabled = activeEditor == null || activeEditor.getSettings().isVirtualSpace();
+
+ final EditorSettingsExternalizable settings = EditorSettingsExternalizable.getInstance();
+ if (settings == null) return;
+
+ String stripTrailingSpaces = settings.getStripTrailingSpaces();
+ final boolean doStrip = !stripTrailingSpaces.equals(EditorSettingsExternalizable.STRIP_TRAILING_SPACES_NONE);
+ final boolean inChangedLinesOnly = !stripTrailingSpaces.equals(EditorSettingsExternalizable.STRIP_TRAILING_SPACES_WHOLE);
+
+ int[] caretLines;
+ if (activeEditor != null && inChangedLinesOnly && doStrip && !isVirtualSpaceEnabled) {
+ List<Caret> carets = activeEditor.getCaretModel().getAllCarets();
+ caretLines = new int[carets.size()];
+ for (int i = 0; i < carets.size(); i++) {
+ Caret caret = carets.get(i);
+ caretLines[i] = caret.getLogicalPosition().line;
+ }
+ }
+ else {
+ caretLines = ArrayUtil.EMPTY_INT_ARRAY;
+ }
+ ((DocumentImpl)document).clearLineModificationFlagsExcept(caretLines);
+ }
+
+ public static boolean stripIfNotCurrentLine(@NotNull Document document, boolean inChangedLinesOnly) {
+ if (document instanceof DocumentWindow) {
+ document = ((DocumentWindow)document).getDelegate();
+ }
+ if (!(document instanceof DocumentImpl)) {
+ return true;
+ }
+ DataContext dataContext = DataManager.getInstance().getDataContext(IdeFocusManager.getGlobalInstance().getFocusOwner());
+ boolean isDisposeInProgress = ApplicationManager.getApplication().isDisposeInProgress(); // ignore caret placing when exiting
+ Editor activeEditor = isDisposeInProgress ? null : CommonDataKeys.EDITOR.getData(dataContext);
+
+ // when virtual space enabled, we can strip whitespace anywhere
+ boolean isVirtualSpaceEnabled = activeEditor == null || activeEditor.getSettings().isVirtualSpace();
+
+ boolean markAsNeedsStrippingLater;
+
+ if (activeEditor != null && activeEditor.getCaretModel().supportsMultipleCarets()) {
+ List<Caret> carets = activeEditor.getCaretModel().getAllCarets();
+ List<VisualPosition> visualCarets = new ArrayList<VisualPosition>(carets.size());
+ int[] caretOffsets = new int[carets.size()];
+ for (int i = 0; i < carets.size(); i++) {
+ Caret caret = carets.get(i);
+ visualCarets.add(caret.getVisualPosition());
+ caretOffsets[i] = caret.getOffset();
+ }
+
+ markAsNeedsStrippingLater = ((DocumentImpl)document).stripTrailingSpaces(activeEditor.getProject(), inChangedLinesOnly, isVirtualSpaceEnabled, caretOffsets);
+
+ if (!ShutDownTracker.isShutdownHookRunning()) {
+ final Iterator<VisualPosition> visualCaretIterator = visualCarets.iterator();
+ activeEditor.getCaretModel().runForEachCaret(new CaretAction() {
+ @Override
+ public void perform(Caret caret) {
+ if (visualCaretIterator.hasNext()) {
+ caret.moveToVisualPosition(visualCaretIterator.next());
+ }
+ }
+ });
+ }
+ }
+ else {
+ VisualPosition visualCaret = activeEditor == null ? null : activeEditor.getCaretModel().getVisualPosition();
+ int caretLine = activeEditor == null ? -1 : activeEditor.getCaretModel().getLogicalPosition().line;
+ int caretOffset = activeEditor == null ? -1 : activeEditor.getCaretModel().getOffset();
+
+ final Project project = activeEditor == null ? null : activeEditor.getProject();
+ markAsNeedsStrippingLater = ((DocumentImpl)document).stripTrailingSpaces(project, inChangedLinesOnly, isVirtualSpaceEnabled,
+ caretLine, caretOffset);
+
+ if (!ShutDownTracker.isShutdownHookRunning() && activeEditor != null) {
+ activeEditor.getCaretModel().moveToVisualPosition(visualCaret);
+ }
+ }
+ return !markAsNeedsStrippingLater;
+ }
+
+ public void documentDeleted(@NotNull Document doc) {
+ myDocumentsToStripLater.remove(doc);
+ }
+
+ @Override
+ public void unsavedDocumentsDropped() {
+ myDocumentsToStripLater.clear();
+ }
+}